From 4b881dd264fe13243630f0fb4348ee758e82f417 Mon Sep 17 00:00:00 2001 From: Maurice de Bruyn Date: Sun, 11 Oct 2026 21:25:10 +0200 Subject: [PATCH] docs: rewrite guidance in plain English --- .changeset/README.md | 14 +- .intent/review-state.json | 514 ++++++++++-------- GLOSSARY.md | 38 +- README.md | 60 +- _artifacts/domain_map.yaml | 29 +- _artifacts/skill_spec.md | 186 ++++--- .../content/docs/concepts/client-lifecycle.md | 67 +-- .../docs/concepts/data-normalization.md | 46 +- .../docs/concepts/http-utility-tree.md | 61 ++- .../docs/concepts/queries-and-mutations.md | 52 +- .../content/docs/concepts/rpc-utility-tree.md | 12 +- .../content/docs/concepts/semantic-keys.md | 59 +- apps/docs/src/content/docs/examples.md | 58 +- .../docs/examples/cancellable-commands.md | 72 +-- .../compatibility-and-stability.md | 17 +- .../docs/getting-started/feature-support.md | 26 +- .../docs/getting-started/http-quick-start.md | 35 +- .../docs/getting-started/installation.md | 15 +- .../docs/getting-started/quick-start.md | 45 +- .../src/content/docs/guides/angular-query.md | 34 +- .../src/content/docs/guides/cache-filters.md | 78 +-- .../src/content/docs/guides/cancellation.md | 36 +- .../docs/guides/choose-stream-history.md | 46 +- .../docs/guides/conditional-queries.md | 57 +- .../docs/guides/custom-key-encoders.md | 50 +- .../content/docs/guides/handle-failures.md | 25 +- .../src/content/docs/guides/http-metadata.md | 16 +- .../docs/guides/http-queries-and-mutations.md | 16 +- .../docs/guides/hydrate-query-views.md | 52 +- .../content/docs/guides/hydrate-unary-data.md | 78 +-- .../docs/src/content/docs/guides/mutations.md | 8 +- .../content/docs/guides/optimistic-writes.md | 49 +- .../src/content/docs/guides/pagination.md | 14 +- apps/docs/src/content/docs/guides/queries.md | 10 +- .../src/content/docs/guides/query-defaults.md | 26 +- .../src/content/docs/guides/react-query.md | 59 +- .../docs/guides/refresh-from-events.md | 59 +- .../src/content/docs/guides/retry-queries.md | 34 +- .../src/content/docs/guides/solid-query.md | 24 +- .../content/docs/guides/stream-snapshots.md | 34 +- .../docs/src/content/docs/guides/streaming.md | 18 +- .../docs/guides/suspense-and-recovery.md | 38 +- .../src/content/docs/guides/svelte-query.md | 26 +- .../docs/guides/switch-cache-owners.md | 74 +-- .../src/content/docs/guides/tanstack-start.md | 44 +- .../src/content/docs/guides/trace-streams.md | 42 +- .../docs/src/content/docs/guides/vue-query.md | 24 +- .../content/docs/guides/websocket-clients.md | 44 +- apps/docs/src/content/docs/index.mdx | 30 +- .../src/content/docs/integrations/index.md | 57 +- .../reference/browser-and-worker-hosts.md | 32 +- .../reference/compatibility-and-limits.md | 96 ++-- .../docs/src/content/docs/reference/errors.md | 63 +-- .../docs/reference/external-http-clients.md | 78 +-- .../src/content/docs/reference/factory.md | 16 +- .../docs/reference/generated-builders.md | 154 +++--- .../content/docs/reference/http-builders.md | 271 ++++----- .../content/docs/reference/http-factory.md | 96 ++-- .../src/content/docs/reference/performance.md | 30 +- .../content/docs/reference/public-exports.md | 144 ++--- .../content/docs/reference/type-inference.md | 61 ++- .../0001-target-effect-4-release-candidate.md | 20 +- ...let-call-sites-choose-query-or-mutation.md | 12 +- ...ject-dotted-rpc-tags-into-utility-paths.md | 22 +- ...-shaped-options-and-certify-react-first.md | 22 +- ...preserve-the-cause-in-one-error-wrapper.md | 20 +- ...anonical-semantic-json-for-payload-keys.md | 19 +- ...keep-the-core-factory-lifecycle-neutral.md | 12 +- ...08-use-flat-prefix-matchable-key-tuples.md | 15 +- ...0009-build-an-eager-frozen-utility-tree.md | 14 +- ...010-reuse-tanstack-query-core-as-a-peer.md | 14 +- docs/adr/0011-publish-esm-only.md | 14 +- ...rmalize-undefined-query-success-to-null.md | 10 +- ...vite-plus-workspace-with-a-root-package.md | 13 +- ...quire-query-stable-payload-construction.md | 14 +- ...5-centralize-the-toolchain-in-vite-plus.md | 15 +- .../0016-automate-releases-with-changesets.md | 14 +- ...-new-dependency-releases-for-seven-days.md | 10 +- ...-infinite-query-builders-for-unary-rpcs.md | 12 +- ...9-host-effect-rpc-inside-tanstack-start.md | 8 +- ...ate-accumulated-and-live-stream-queries.md | 22 +- ...ty-construction-through-private-modules.md | 12 +- ...-http-utilities-with-separate-key-roots.md | 13 +- ...xpose-buffered-multipart-http-mutations.md | 8 +- ...ject-http-streams-and-buffered-metadata.md | 14 +- ...ed-message-clients-for-server-local-rpc.md | 17 +- ...ransport-aggregation-in-the-application.md | 8 +- skills/effect-api-query/SKILL.md | 171 +++--- .../references/cache-workflows.md | 206 +++---- .../references/frameworks-and-hosts.md | 168 +++--- skills/effect-api-query/references/http.md | 152 +++--- .../references/hydration-and-ssr.md | 118 ++-- .../references/query-patterns.md | 193 +++---- skills/effect-api-query/references/rpc.md | 79 +-- 94 files changed, 2648 insertions(+), 2462 deletions(-) diff --git a/.changeset/README.md b/.changeset/README.md index ab75e68..dc544cb 100644 --- a/.changeset/README.md +++ b/.changeset/README.md @@ -1,18 +1,18 @@ # Changesets -Add a non-empty Changeset when a pull request changes the published package's runtime behavior, types, -exports, peer or runtime dependency ranges, or metadata that affects installation: +If a pull request changes the published package's runtime behavior, types, exports, or peer or runtime dependency ranges, add a non-empty Changeset. +Changes to metadata that affects installation also require a non-empty Changeset: ```sh vp run changeset ``` -Choose the pre-1.0 bump deliberately and describe the consumer-visible change. The version workflow -formats generated changelogs and manifests through Vite+'s Oxfmt task, and the Changeset Bot leaves -an advisory pull-request comment when changes to `src/**` or the root `package.json` may need a Changeset. +Choose the pre-1.0 version bump and describe the change that consumers see. +The version workflow uses Vite+'s Oxfmt task to format generated changelogs and manifests. +If changes to `src/**` or the root `package.json` can require a Changeset, the Changeset Bot leaves an advisory pull-request comment. -Documentation, examples, tests, internal tooling, and CI do not require a Changeset. Do not add an -empty Changeset as a process marker; explain an intentional omission during review instead. +Documentation, examples, tests, internal tooling, and CI do not require a Changeset. +Explain an intentional omission during review. Do not add an empty Changeset as a process marker. Only the root `effect-api-query` package is versioned and published. The private example workspace packages remain unversioned. diff --git a/.intent/review-state.json b/.intent/review-state.json index 74c50f9..01b88e9 100644 --- a/.intent/review-state.json +++ b/.intent/review-state.json @@ -3,86 +3,86 @@ "baseline": "5b91ddb1de13970365669c887b9d767ea417fbd4", "items": { "skill:skills/effect-api-query/SKILL.md": { - "fingerprint": "1725c371227ab1e1a3a584b649a1456a02732ecb7d4a8bd6074d607ad295519c", - "snapshot": { - "GLOSSARY.md": "ad19a69727108dd047208b6e76e05f5b1d241620ed75cb241a62fb4c068dc792", - "README.md": "81ca8fe3c5f95884e69a1f885c4e644eff70e0f3c557b45785834719e456caec", - "apps/docs/src/content/docs/concepts/client-lifecycle.md": "fcbaca84f266060b946eed9cbf9d0b9815352b7b1d6ddaff5803b29484345380", - "apps/docs/src/content/docs/concepts/data-normalization.md": "f1505ec3eb35a33fac289706c2cc318a2ffb80555e3302ca102e67e21c9012da", - "apps/docs/src/content/docs/concepts/http-utility-tree.md": "ed03938167f31822b76efbeac381fd3f8d297061fe567928ee022f8dbfadb9b4", - "apps/docs/src/content/docs/concepts/queries-and-mutations.md": "204141ba9e3b52e8e0b2d994ef4381cfcecf898d0bd011866f8cbfc8e89cb15c", - "apps/docs/src/content/docs/concepts/rpc-utility-tree.md": "fd5fcc232279e9577b8b2faf321a85e9a340e6090ea5887bbab208cca2d24a4e", - "apps/docs/src/content/docs/concepts/semantic-keys.md": "4fcc7437b4c0698f590926545e41b68eed1094027fb18a8834fb695db63262c6", - "apps/docs/src/content/docs/examples.md": "4035adf9d277a7a3d75b22be8147ee6acb3329e6e96ca7c76b9d4fc052758239", - "apps/docs/src/content/docs/examples/cancellable-commands.md": "c175cbd7f15b2322b3039a95aba937a440ae4198794195b6db7419b2a3acb245", - "apps/docs/src/content/docs/getting-started/compatibility-and-stability.md": "74f9d59c908fe6ab70ccfb8940c1f976e060b38a05cc6bbe75f72403d3b3ed7b", - "apps/docs/src/content/docs/getting-started/feature-support.md": "5a6b57b8e539937b203da9bb571d31da2169a014f6f0ee6d0625627a8b0f7525", - "apps/docs/src/content/docs/getting-started/http-quick-start.md": "9df7c42c5f87270a065b4cfafb2f717c7735db79268fd8b9ed30b73b03fa9cc2", - "apps/docs/src/content/docs/getting-started/installation.md": "fde89cc5c10d005672db696bdc8016deb42d86977fca1b89cfe6315c760bf2d3", - "apps/docs/src/content/docs/getting-started/quick-start.md": "5d0b2469e2e960c87110e0bfe404ad4137f6df1362c1e72989417db87a97b6fb", - "apps/docs/src/content/docs/guides/angular-query.md": "82b6cc47488db0b166b0f79597526e26274823b48377482fb52949a00c5a9515", - "apps/docs/src/content/docs/guides/cache-filters.md": "ac56ebb6d8949bec515be80850a5386d79eb9dc38533286ad3fa638f82654418", + "fingerprint": "f4fa1b203aa1f5bcd140c70f6fda9317de913d163ccdb9a73aac5ff5d1c7f249", + "snapshot": { + "GLOSSARY.md": "60e4919b11d134139ac1dcb9b796903caa2b672fd474a2b83338547802194a08", + "README.md": "501f99e7854c7e4e4154ad0cdc23afc6680e1cd27be38d2145e684d48884cfc6", + "apps/docs/src/content/docs/concepts/client-lifecycle.md": "10da4a8517faea055cea83f158bb68cf3c8c60b0a1fc4cb53bf9900a846db91f", + "apps/docs/src/content/docs/concepts/data-normalization.md": "c67ac43accbca0b1982da942a7e5bb32c68e7cdd0d94a6a7e4e8e74f15a8fba9", + "apps/docs/src/content/docs/concepts/http-utility-tree.md": "adb5974bce5b1061ef522018e11554b03b13ce8ecf82fdfa40a100f0f903ad22", + "apps/docs/src/content/docs/concepts/queries-and-mutations.md": "435f1b7eb715156aa838954da5e90ecdccbd81ca32af3a8c095dd6b0bcb74faf", + "apps/docs/src/content/docs/concepts/rpc-utility-tree.md": "507544a84c4e5159a69fcc74588bffb0e3fd9217ec5b71fe2cde1bd39c01d8b9", + "apps/docs/src/content/docs/concepts/semantic-keys.md": "f5c23587532f038e1f8933590fe747486c82e6c1da3c8ad747eb647c03bf9487", + "apps/docs/src/content/docs/examples.md": "6fccd10d78463e14df3300312fb4e8714b3c607649880b48d0e1514fc4c34408", + "apps/docs/src/content/docs/examples/cancellable-commands.md": "272c01ab8cc1b63b1d2c064fd5b97da7505fe721da07c9301a2e4f779ac8a73a", + "apps/docs/src/content/docs/getting-started/compatibility-and-stability.md": "dbb168ad0411e22d0989c7421ff9576762315c82ce270bf6b65e876d1ec96bdd", + "apps/docs/src/content/docs/getting-started/feature-support.md": "0c44d3f5dac0be81fb154e0663368795d7794faf07fee9e79c75844a23fa93f4", + "apps/docs/src/content/docs/getting-started/http-quick-start.md": "791c00beac0d39ef5f46f73e0af3033b29be7274f9a1cbfe39f781975ce7d7cf", + "apps/docs/src/content/docs/getting-started/installation.md": "2fb452a210dc1cb6488651a31442c8acde35e1b5ebde8253810dcf258e6dabe3", + "apps/docs/src/content/docs/getting-started/quick-start.md": "148f23bc4320388882b3bb9a473e27bdc510e12515f73ba2af56ca854444aa39", + "apps/docs/src/content/docs/guides/angular-query.md": "3d6ba5560db5c4f03969da124cce568ac883bd148ad5bafe5045d53f3059be51", + "apps/docs/src/content/docs/guides/cache-filters.md": "71ce56ff7f9804ea92e43fdd0c37928f4c0d7c74042eaacc08f0c034f689db2c", "apps/docs/src/content/docs/guides/cache-management.md": null, - "apps/docs/src/content/docs/guides/cancellation.md": "8566563c90d913c4db16435d314eba1ec53d6873b30aa5652097d0994c50cc4c", - "apps/docs/src/content/docs/guides/choose-stream-history.md": "6996df3a8b18c4fadbcc76a3635a8a5408f155b4068509d4801b38afa7f22f16", - "apps/docs/src/content/docs/guides/conditional-queries.md": "7cc52d18217653c63699e78ecce3efb78c484e57ba75a92071df5d16b9a40ac8", - "apps/docs/src/content/docs/guides/custom-key-encoders.md": "e48f1768db1c9e8ab3d5915593dc6de9089c35c039fb16e7ca04a4326a4df8bf", - "apps/docs/src/content/docs/guides/handle-failures.md": "7bf37fc98c4a4eab109ead80d24febe4dda968267f07e7f3d665297c746a9c2b", - "apps/docs/src/content/docs/guides/http-metadata.md": "b0b45e8c3af5ada0d64cf61bd5bca161186a604c30d3e9fc63ba9090c590e542", - "apps/docs/src/content/docs/guides/http-queries-and-mutations.md": "23edf1a1c71a29561d9c87cc7fca1f7c8ed370d6e62be5719dcf54d533c420b9", - "apps/docs/src/content/docs/guides/hydrate-query-views.md": "d71f573e4c6e6815e51f7a63080b773655f90cf9ef07155d293f1550eac091d4", - "apps/docs/src/content/docs/guides/hydrate-unary-data.md": "0cf27b204e709f8a753fd61fd1510073404611e53a9fd6e82e8122af7e68f2fa", - "apps/docs/src/content/docs/guides/mutations.md": "773d6720894a83083bcd54adb55b632a9902894165a7c147c1266620774d8f21", - "apps/docs/src/content/docs/guides/optimistic-writes.md": "8d5001c0c1caa2f4cc25ce10955791eaef2f032327fc9c8a743173ffd60b68b5", - "apps/docs/src/content/docs/guides/pagination.md": "95a82e2cc2a972080152ac0816e06262ad63c7c0a7f9c8a60b3f836a42120a1a", - "apps/docs/src/content/docs/guides/queries.md": "6fb845e9d9d86a94b4df323583c9f721c171d135e2cb64f184f8a6fea22350e7", - "apps/docs/src/content/docs/guides/query-defaults.md": "74b7f9cd1c29b6bfa3777e8b48cc3dc6319856b48029d052adb4de34a9f5ddaf", - "apps/docs/src/content/docs/guides/react-query.md": "c8b23f26fd836a719cd3a03dc53c9abf46b1e607383cf0f66a59355bb4953671", - "apps/docs/src/content/docs/guides/refresh-from-events.md": "a8e680ae9060a573336829b94dbf77dd32385671d48cd4aa0f092a1374444cd6", - "apps/docs/src/content/docs/guides/retry-queries.md": "4326d4e0cec0ca0067182020f68e4db0f634ecefedcc5cadd0db3e90e3709684", - "apps/docs/src/content/docs/guides/solid-query.md": "6ce866f49974edc7f5db579d08134734544de1137d5a9e5efdf49bc62d12b7f2", - "apps/docs/src/content/docs/guides/stream-snapshots.md": "91e22e85c11d8822e25e5abb7b872b78f07af79fdb16eb2690baf8226fdbd9f7", - "apps/docs/src/content/docs/guides/streaming.md": "2d43cc0ac6f007f31aabfe01200d895827cb14ec1166324eb9661d146cda240a", - "apps/docs/src/content/docs/guides/suspense-and-recovery.md": "a5f92087e5006d2162ab623cdc39d98a41e13b5967e7483e3db7fa46e436e730", - "apps/docs/src/content/docs/guides/svelte-query.md": "967257cd51f59723bd641c8553a4e50b931258e79f24d60f47fdfb3035ffe6a8", - "apps/docs/src/content/docs/guides/switch-cache-owners.md": "cc209f13becec305af60c9f04fe29f85a2ca6db3bc11601cf09083bc2583d198", - "apps/docs/src/content/docs/guides/tanstack-start.md": "7ec04e78ab8798f8cd89839f2a07b466f0c9834557126799373940b161a95b0b", - "apps/docs/src/content/docs/guides/trace-streams.md": "4213d016728cfc45a31f7d7cb125362f7a4edf46bf6e9c40b325642e800f5ba1", - "apps/docs/src/content/docs/guides/vue-query.md": "bd8b50c940d0493780a60ae0cd80fc2843b2ebb22c2d3c9bc70653e0379d4155", - "apps/docs/src/content/docs/guides/websocket-clients.md": "1cd31655e433ced7b24795255cb03280b27734eda58afeb9730a42bfad433c43", - "apps/docs/src/content/docs/index.mdx": "b5bcd9d26ee0b8ea181b0c063afbae9ee057e31db9a2b3252c13589b5847339a", - "apps/docs/src/content/docs/integrations/index.md": "7ee02cd3d7d1d212ac0e539dc6dde5bf928d906797abcac34da1e7ec1352785a", - "apps/docs/src/content/docs/reference/browser-and-worker-hosts.md": "70743a4429a39937515c8d5fee79ca75714a92dd526f1093f9fa8ea12fbde984", - "apps/docs/src/content/docs/reference/compatibility-and-limits.md": "4f60753887b52c33a47b993f0aaaf75832edaeb09d082f2664e2e5e512e568a3", - "apps/docs/src/content/docs/reference/errors.md": "57b90eca4db33880af66063e8211b4a39d0850af811beafc8711bb01860063d5", - "apps/docs/src/content/docs/reference/external-http-clients.md": "9bfef63f64b378847d99092f088492f989ba0577810e6aa07bb194d55ca5fcbb", - "apps/docs/src/content/docs/reference/factory.md": "fb8e57908ccb82d57b3b29664034b3a210945b9bd39e030cd9613015ea3b0c37", - "apps/docs/src/content/docs/reference/generated-builders.md": "0c0406824a2f906ec4d5601ab068d6c1d2b35ce8b9602618f992eff7de8824f2", - "apps/docs/src/content/docs/reference/http-builders.md": "153a63a31bdfaa8411a4af067d555e7fe1af037c9d2f798a7f7a21b4e4be0a86", - "apps/docs/src/content/docs/reference/http-factory.md": "d40fd0597a9d784e21bf7f80ec9cf56008511f472625e572ffded61db0972f68", - "apps/docs/src/content/docs/reference/performance.md": "2f68c31cae28de0a84b1ce77f065481d37899171b78d9593ff65e017ff75d97a", - "apps/docs/src/content/docs/reference/public-exports.md": "9550217e7724f7d15542ff83e0a268bfe7274f7af5a4c3440ef0ee418607a6d9", - "apps/docs/src/content/docs/reference/type-inference.md": "1edcf7de07709644784a1f7dcc5462f0eb1e01e168f30ba9b1db102ebd424fd6", - "docs/adr/0001-target-effect-4-release-candidate.md": "44119ba9104f767c5804c5026c4a21a78a8367f6d9ff5a9035bb3f423969fbb3", - "docs/adr/0002-let-call-sites-choose-query-or-mutation.md": "4244d48ff136eea562aa2f94a0b6b1ab58412796f3ece47a3cf5b43f746e11f5", - "docs/adr/0003-project-dotted-rpc-tags-into-utility-paths.md": "a99856e94af4004484fed939d46ae4388363031cf396b07f04a92a7119fec2fe", - "docs/adr/0004-build-core-shaped-options-and-certify-react-first.md": "0e47069738c3792430c92b396068ac269139356c83738453889517d97e22f190", - "docs/adr/0005-preserve-the-cause-in-one-error-wrapper.md": "1e04885542f22f166b5daa913ae2dae121c407baa775a2c4a16590f96857e3fe", - "docs/adr/0006-use-canonical-semantic-json-for-payload-keys.md": "694d8665938af25abb91ada71ea9c95cb77dd4ea90019adb2f642e16a88948c7", - "docs/adr/0007-keep-the-core-factory-lifecycle-neutral.md": "a83dc0d6061ac0328a3ee6075aa872a55feb4f0154edf6dae964e74276bb15b5", - "docs/adr/0008-use-flat-prefix-matchable-key-tuples.md": "7fb791e73e0266afd8583e45059aebcc10bbd9704d625bdefcafec1481a46cf6", - "docs/adr/0009-build-an-eager-frozen-utility-tree.md": "bc3b08a3385f1dc8874d81f867f15ef6c30b203c94936fd991d2941fd5bacc27", - "docs/adr/0010-reuse-tanstack-query-core-as-a-peer.md": "633a1ce44c8d58b29096f540871b61566352462e1d4bd5335162b3e08652527d", - "docs/adr/0012-normalize-undefined-query-success-to-null.md": "5d076f168223f67a205138d3e258f1f3aac9dfe5d607ffe8efd12a71322ef022", - "docs/adr/0014-require-query-stable-payload-construction.md": "2f4381fb3e7406304ff3b914db541419f799f8a71d1d527b37a9e971e9949e47", - "docs/adr/0015-centralize-the-toolchain-in-vite-plus.md": "d2001383bc0f92363289c04b6b8df26acb5b0f5dfc0163b2a838f2eee8057b75", - "docs/adr/0020-generate-accumulated-and-live-stream-queries.md": "5e4f5c92e08ffb769137e930908dded993f928a497b018f9c6e4fd4f12232727", - "docs/adr/0021-share-utility-construction-through-private-modules.md": "821720a3eee166d4b7d42b6f5c3906bb29dff339b4815e9ed1c516bc5af66fd4", - "docs/adr/0022-add-buffered-http-utilities-with-separate-key-roots.md": "f1fb5d91cd91a70b356bc98b84176920ec822315dbdeaf5e247921853598b62f", - "docs/adr/0023-expose-buffered-multipart-http-mutations.md": "b72f20cb513d2a7db94b2dbe27576352048ba22288ad6eac6e80d1a7c81cefb8", - "docs/adr/0024-project-http-streams-and-buffered-metadata.md": "2797a5a4a751c5e37c38aa454c14b99ce1615870ffa13cfcedb98cca8a0af633", - "docs/adr/0025-use-public-decoded-message-clients-for-server-local-rpc.md": "36df8e1227af22018d5a9063699986ceb8dcfd4c8bb5615aa62557f2ec36e111", - "docs/adr/0026-keep-rpc-transport-aggregation-in-the-application.md": "b73af29d8f73a36f61f110ad8778e045d37253fe42a8888883e78089b93e955e", + "apps/docs/src/content/docs/guides/cancellation.md": "f5a1e5863b8f173470ed78616ede0b8bfb27488bb60252ad48dffc622a339ccb", + "apps/docs/src/content/docs/guides/choose-stream-history.md": "c138808345ba4b1220ba24deef63150ad188747ade7213d7243d00d08ef22cf2", + "apps/docs/src/content/docs/guides/conditional-queries.md": "9b1d0fd9449bf92c500dd8b60d1a5a01e5675b0e223d3c789ca04a7150dfbcd6", + "apps/docs/src/content/docs/guides/custom-key-encoders.md": "8bdcb04d74913f0906069d301b0c477413925b66be5e28d139b35a9797216ec6", + "apps/docs/src/content/docs/guides/handle-failures.md": "633c88a58464798f5fb46d2059b63927cab2735aa552ac4b1d0b222579272ade", + "apps/docs/src/content/docs/guides/http-metadata.md": "c634f1c609500456551b48142396a9db1f7ad0e2c6a692c4dec547feb177ec1a", + "apps/docs/src/content/docs/guides/http-queries-and-mutations.md": "b5c646131aa5c3c641579c5e75963052ae942cac9a0b427f763108ef4d45e648", + "apps/docs/src/content/docs/guides/hydrate-query-views.md": "d69329e9636fdc0f5a4249e8804da8b15e5793c4fde00b2148b6419baf050cdc", + "apps/docs/src/content/docs/guides/hydrate-unary-data.md": "a935103d99fd3917392e24dec0bf3e53d118a5d4af6b966a9d8a8e876f76394c", + "apps/docs/src/content/docs/guides/mutations.md": "9126b133c1fb97d8ccf1a6151cc37c1677428b3668d0585588d416254c01daea", + "apps/docs/src/content/docs/guides/optimistic-writes.md": "d5c1cbca4f484c38c6c4fd86ec38a25c96da0a6320cf7dbe91eb24fe02179d88", + "apps/docs/src/content/docs/guides/pagination.md": "3f126136723ff6848a34d0392f3669d1190a884299256c3f1b13615028ec13f8", + "apps/docs/src/content/docs/guides/queries.md": "26f8f6ae6ea5e745c155400759faa5680b5735cfae9046e3e9edc1105c1fa5e6", + "apps/docs/src/content/docs/guides/query-defaults.md": "52afde31f49c855f9fc83a43d85747a052d9cbfbdb5fc5aec4a08aee9c92c40e", + "apps/docs/src/content/docs/guides/react-query.md": "b1cf8b84280357141df3b478fa45a2e322b8473f06b706199d389690a40bbb93", + "apps/docs/src/content/docs/guides/refresh-from-events.md": "6167c023679b311c6292466b8fe134bc5ab035ff7df55fe9e73c8658a1fe20b0", + "apps/docs/src/content/docs/guides/retry-queries.md": "a1ab846da36b796f695edb4b1a0033a508901feb128cab4a589b20f962a7bd4b", + "apps/docs/src/content/docs/guides/solid-query.md": "1d244991dfcecdc6605613f922e0a33bfa16dd2abfed7bd7a36f28b076626ea0", + "apps/docs/src/content/docs/guides/stream-snapshots.md": "1f397caa3bf6651b02f8c445bb34d0cf66f19020b70da0d353217e61163de756", + "apps/docs/src/content/docs/guides/streaming.md": "1d4915f53e1b1e3ffd5f997116efd03adc65a3481235fda346f87f5ae61f55a5", + "apps/docs/src/content/docs/guides/suspense-and-recovery.md": "2b86a4b9bae6e530c6ee096093673f2a204cb7ac86e956b1a217f0a888c5b161", + "apps/docs/src/content/docs/guides/svelte-query.md": "bd29928e2be9618aad82e70235358aa297bf132d345d6b796126a746015a6c73", + "apps/docs/src/content/docs/guides/switch-cache-owners.md": "18e4ce7507a1a3572612f9c7de282dac2a23507cf9111cdb7756923903e1674e", + "apps/docs/src/content/docs/guides/tanstack-start.md": "3a8a702708a4b1bd9b2a93f22d20e980d55fcd15d5c5a986033da1c5d542b5c8", + "apps/docs/src/content/docs/guides/trace-streams.md": "b90ed9a5922a1dfbce58e90e039e149cae9efd28c11a98e79f81203b0fc302c4", + "apps/docs/src/content/docs/guides/vue-query.md": "c89cb2205e7fed5a0e9b707a41b8dcebc3381cdae8a79a1be6dc6421a30ed39c", + "apps/docs/src/content/docs/guides/websocket-clients.md": "c247492f252393d7bc57a0e107b95619c6899b79d6e25ecd884b10cb3d8bd2a7", + "apps/docs/src/content/docs/index.mdx": "f67c034d8ba493f6ec6ca4e1268adff194180b2f975589dbb8b7d66b837e866d", + "apps/docs/src/content/docs/integrations/index.md": "a67e580f5d8113cde0d1ac422cbc1270a5c65c7d334a4d1acfbae77a733a6375", + "apps/docs/src/content/docs/reference/browser-and-worker-hosts.md": "bdb546e21f000f227c076bcd383a425a9480c3ee515f5a831fd9e7cc5e663699", + "apps/docs/src/content/docs/reference/compatibility-and-limits.md": "d921d2fe2578a8fa545ecb8bfa715b705db31db031b24222c6660df6983bd471", + "apps/docs/src/content/docs/reference/errors.md": "67df05b0ae18702347c23ad08458b970ddbbb27bdf2bc616d90ca96bb630815b", + "apps/docs/src/content/docs/reference/external-http-clients.md": "a03f55ea87c0b28beac94775aeaac8e21b6b63d2348cedb441b50c9a33c08a3d", + "apps/docs/src/content/docs/reference/factory.md": "359ec1488dc6d551d84413faaa016d24954473ccc0705a6ee988dc512e3c506b", + "apps/docs/src/content/docs/reference/generated-builders.md": "d1b39925363e7d13b815c3e5312692cbee2a0665ee8afc6b99362b2f33d3aa58", + "apps/docs/src/content/docs/reference/http-builders.md": "fedf31eb11b3532aee81955939b5a40e86a0dea58334a423c695963782b439b0", + "apps/docs/src/content/docs/reference/http-factory.md": "ad8d90aede30203ebbd7c9894f5896c8db748ed0b20b6e6943c61425c2e5b925", + "apps/docs/src/content/docs/reference/performance.md": "9fcd785bf8df6510e69541da71c2600cb7762899a886d258b1a99eb42dcfdb29", + "apps/docs/src/content/docs/reference/public-exports.md": "141219f987b5c33f6b1f0122063634c2cd2c8bba9f965cad88712cb097d617af", + "apps/docs/src/content/docs/reference/type-inference.md": "9b8208d0d6cb49894416bde7161ea74463f1d3e8fbb1d243ed1a11e2749e3ed0", + "docs/adr/0001-target-effect-4-release-candidate.md": "df4be0035618ca4b6ca5816cb41fdeba182279a07db35caa2b5a2d7128d7d786", + "docs/adr/0002-let-call-sites-choose-query-or-mutation.md": "4d8c0e6ac2d6aa68c9a295025463854771720b4217b9a0f8f832ec6f5813a090", + "docs/adr/0003-project-dotted-rpc-tags-into-utility-paths.md": "2cc3876c9b9d57e9ffee57e8a6c98a0519ef21d3674150b8af283aa167bf5458", + "docs/adr/0004-build-core-shaped-options-and-certify-react-first.md": "051ea72c3a257e16fecafbb4dc1970eaecaf921729e3ddfa843440368470ccee", + "docs/adr/0005-preserve-the-cause-in-one-error-wrapper.md": "3fafcc3240839d332ffeb8c948711b5d0f69aecd3c9209bcd63871390d382fbb", + "docs/adr/0006-use-canonical-semantic-json-for-payload-keys.md": "c34cb02991666a22cd1e89f068b15f8f95d31728e7bcaac2647417b7f00a8b2f", + "docs/adr/0007-keep-the-core-factory-lifecycle-neutral.md": "a07e900d13418d9d6ff11bba41649e44cc9e5a5cd1606d919e333075ed03163d", + "docs/adr/0008-use-flat-prefix-matchable-key-tuples.md": "981b875f0b90111382290be898ccac2df125a7b9ca9220c6a3f53e9f1b88a3b0", + "docs/adr/0009-build-an-eager-frozen-utility-tree.md": "dec654dd6e4e4e52867188f681e2df23f1c9f1fc00cde0f0cf9736ca27a683ac", + "docs/adr/0010-reuse-tanstack-query-core-as-a-peer.md": "fa4a4ee5204ad923395b578633b2982e586c2960bf733abbd18ab55bc84a8918", + "docs/adr/0012-normalize-undefined-query-success-to-null.md": "989af99cea4083fd55c13fa37e383f6f33f4929ba2f20aa68bf7c8a6d6885f44", + "docs/adr/0014-require-query-stable-payload-construction.md": "62ee6663061a60b23396345669d42514db8aa7bad3c1a3d4fbe8cd7e3230a9b8", + "docs/adr/0015-centralize-the-toolchain-in-vite-plus.md": "c9179f7fd8cd90fa2dc5e51eb95f60180f399fd6df3202f8020ab40352a1bff1", + "docs/adr/0020-generate-accumulated-and-live-stream-queries.md": "76788b314e904c911bb0814ea4a9bcc053faebcda134780f2b4d09fde83e7990", + "docs/adr/0021-share-utility-construction-through-private-modules.md": "5bde2e0f2b1dc53103eb21094b52f22757386413e25268066c84f27adcf7197f", + "docs/adr/0022-add-buffered-http-utilities-with-separate-key-roots.md": "3472a800096e8e60b5e1861305792ff2667ba84143ced53c03f23899dc76fdb3", + "docs/adr/0023-expose-buffered-multipart-http-mutations.md": "ac91d652dde8484f3603754d74bbfb882602d07759f24bea08b5ef723a2e677b", + "docs/adr/0024-project-http-streams-and-buffered-metadata.md": "f11636c13e4685c3728c11e6a659e27bce3655d8083d4e8c4c4eb5a63512d894", + "docs/adr/0025-use-public-decoded-message-clients-for-server-local-rpc.md": "272358c7168709ebb1e827d5ee370d6cb4613efca9f2f462aacc445f2e180d69", + "docs/adr/0026-keep-rpc-transport-aggregation-in-the-application.md": "e50b09a3d14337e3a1e3e2374fa1de162e6b787431bca698eaffba425334e9a0", "examples/server/src/commands.ts": "58f415fe0ab9bb0127c96c11ef0540a29b97d231eebd3a487513f0820a46b3d5", "examples/server/src/diagnostic-operations.ts": "ddf8a0f2367b91ca08d6aaaf58380a51b77a5c51a9a85412bd7e7080ff0453ee", "examples/server/src/domain.ts": "c00fced49e54d7bfff9045479e91aabdbd7c0868cc89afe00bae5473b17d2f82", @@ -139,13 +139,13 @@ "scripts/measure-construction.mts": "10522cb10ab51fd44d8990896a2a73e8bf4d4cd2646441e958b6180cb1afd147", "scripts/verify-docs-examples.mts": "2fac318ffe8e627d0d19c6434171444647f1bc1369024501a3091cdefe06ed72", "scripts/verify-packed-consumer.mts": null, - "skills/effect-api-query/SKILL.md": "41c0a5768977c0e25254f559de4454df7d9dc5fb6075487d65a6a52602b0e7d4", - "skills/effect-api-query/references/cache-workflows.md": "61d3d363b410eac3f52273e642ec2924d29e887f365a7c21a965a52deeded0be", - "skills/effect-api-query/references/frameworks-and-hosts.md": "e84b9179ca0cba6694200f504923906d78b3b7a305315f5d1453dace854aefb5", - "skills/effect-api-query/references/http.md": "4dd64a0fcc036f3ee857ae96d0e0149c729e0ea95dfd79a82a62c76db4619999", - "skills/effect-api-query/references/hydration-and-ssr.md": "1a6ba66187a1c786c09eb08dbd1c0f0e7c04e21d3b4787eb534687a67c1a38f0", - "skills/effect-api-query/references/query-patterns.md": "2b9ff5a7f9857d00dee85842351bc7ff8d3e88a016e1c0056c2ddb513cf53a64", - "skills/effect-api-query/references/rpc.md": "2d2d30f309424f255e3162fd52dbdaf6d3883e40d9eb6f1840eb814424b0abf3", + "skills/effect-api-query/SKILL.md": "fc5169c63789f37e6d0c674f22bf8620759b47bfa85ba2448c6582c03f5f6a1a", + "skills/effect-api-query/references/cache-workflows.md": "2b54d5ee96eb66a73eaa2896dec9ca04cd08b9bb21d7aea692eb99a6b8c0e0ac", + "skills/effect-api-query/references/frameworks-and-hosts.md": "057bee8c6563e086b9fa9580605f6cae79e9ee5d272b18a71841bbe87c0d776f", + "skills/effect-api-query/references/http.md": "e0d7071c1244a2b0ca9bac9ff12f73863763197e3d67a37ca62c96be572f5bcb", + "skills/effect-api-query/references/hydration-and-ssr.md": "2221298e3cb3f029a04f9ebf91254532eb8f8f2e95d0bb368221e055d180bbf1", + "skills/effect-api-query/references/query-patterns.md": "67e42a182c467b5a09cdff139808e6b7f6bbf280e487e59f24b7f1e4d52d7d69", + "skills/effect-api-query/references/rpc.md": "d02e4d75559a1e3d1998ebefc657d3e6d070201715d50352d25f6b6ba542a567", "src/core/inference.ts": "b8345c0961c67ceb41ee18e017302a5ef5d6be91794b861e72bece21ee5e9c30", "src/core/operation.ts": "7dabe4d21070d20534643e5051060c84a09db5a7aa39858e75846043f8a537ea", "src/core/policy.ts": "bebe7a32bb958ba9a1817c6fec6792fd04541f5ae81cd70fa422106ac3cb4073", @@ -236,102 +236,102 @@ "tests/types/tanstack-start-contract.ts": "51681e3d0d7504a614d490f18adb6f44db82d34b9d4d7cb9cf941aea33de284f", "tests/types/type-scale.ts": "a9b307c25c72f3c184eabc8e22ddaeea96feb331c0f8d748773d765d358dbfca" }, - "head": "cb14a01c995cd9dfac5a3c5dc752ea5d71da862f", - "outcome": "no-change", - "reason": "Named contract/client/UI modules clarify existing canonical examples without changing supported inputs, generated options, ready-client acquisition, runner tracking, or cleanup. The shipped RPC/HTTP and framework guidance remains accurate, including the preserved React acquisition anchor; no guidance rewrite is justified.", + "head": "0feea99e2469c98d60567fef8e0e7baf26f69625", + "outcome": "updated", + "reason": "Rewrote the existing skill and all six references with writing-for-agents as the primary guide. Preserved activation metadata, reading triggers, domain terms, ownership, technical constraints, and fourteen task boundaries. Clarified completed buffer testing and made existing verification checks the explicit completion criterion. API recommendations and code examples are unchanged.", "evidence": [ - "Source cb14a01c995cd9dfac5a3c5dc752ea5d71da862f plus current local diff. No public library behavior, types, dependencies, package version, or publishing setup changes.", - "Fresh vp run --no-cache api-types passed package/declaration construction, attw, publint, and actual importing compiler fixtures under TypeScript 7 and 5.9.3. Exact documentation excerpt verification and Astro checks passed; docs-build produced 56 pages with valid internal links.", - "Type-aware lint and checking passed 150 files; repository formatting passed 310 files. The two existing React mounting and stream tracing runtime recipe tests passed after real module extraction.", - "Seven Chromium documentation checks passed against the fresh local build: navigation/search/assets, published registry version, unpublished/network/invalid-version fallbacks, and generated LLM documentation links. Collaborative browser inspection confirmed separate quick-start and React modules, preserved React anchor, and the correct October 11 page-change date.", - "Workflow YAML 1.2 parsing and dependency/branch/artifact inspection passed. Documentation now depends on successful validation on main, reuses its browser-tested artifact, and has no publish dependency; standalone manual main builds check compiler fixtures. No live workflow was dispatched and npm publication remains unconfigured as confirmed by the maintainer.", - "Intent sync reported Nothing to synchronize. Reviewed the owned skill, RPC/HTTP and framework references, and all three planning records against the actual diff. Existing source patterns cover every new fixture and changed canonical guide. Fresh consumer/discovery sessions were not repeated because recommendations and library behavior are unchanged." + "Source HEAD 0feea99e2469c98d60567fef8e0e7baf26f69625, package effect-api-query 0.1.0, and actual uncommitted prose diff. Runtime source, public types, dependencies, examples, and activation metadata are unchanged.", + "Protected-content audit: all 92 in-scope Markdown/MDX documents revised; all 113 code blocks, Markdown link targets, and heading anchors preserved. No prose em/en dashes or semicolons remain. .agents/, docs/agents/, and AGENTS.md match HEAD.", + "vp run --no-cache api-types passed package build, attw, publint, and public API/documentation fixtures under TypeScript 7 and 5.9.", + "vp run --no-cache docs-build passed exact documentation excerpt verification and Astro checks, built 56 pages, and validated all internal links.", + "vp run --no-cache format, git diff --check, and vp exec intent validate skills --check passed. Intent maintainer sync reported nothing to synchronize.", + "Independent prose reviews covered technical conditions, cleanup ordering, key identity, shared callbacks, hydration, and agent-facing ADR/skill instructions. No runtime or recommended API behavior changed. A fresh consumer session and new discovery check were not repeated for this editorial revision." ] }, "planning:_artifacts": { - "fingerprint": "1d9931241eda26f4d331c409c514108ccbfa050512905b381b1750fa81510317", + "fingerprint": "7c3409001b80883133ecd6913a74472651814d90f6e8ecd9087d58c77978e55e", "snapshot": { - "GLOSSARY.md": "ad19a69727108dd047208b6e76e05f5b1d241620ed75cb241a62fb4c068dc792", - "README.md": "81ca8fe3c5f95884e69a1f885c4e644eff70e0f3c557b45785834719e456caec", - "_artifacts/domain_map.yaml": "03379e3cd6338cb4bb20bd15408af2ff86accf9fa009259f715123be50681d3c", - "_artifacts/skill_spec.md": "93dbe6009f951ddb49a350b85ce212f1b092bbd381cf2a1425ab3e243200e199", + "GLOSSARY.md": "60e4919b11d134139ac1dcb9b796903caa2b672fd474a2b83338547802194a08", + "README.md": "501f99e7854c7e4e4154ad0cdc23afc6680e1cd27be38d2145e684d48884cfc6", + "_artifacts/domain_map.yaml": "6f21087e34c10fd44695f70ed8daaf5837ca0eaab6add84e0fc20933565c2e9c", + "_artifacts/skill_spec.md": "82bd6cb4990905ec97eddd0b058c59099687d2dd93ab4314bc70b47f87866694", "_artifacts/skill_tree.yaml": "0ef24d94a4bbc5c4b628892a130c2b36a81a16c5705a48e990919b5eec467a47", - "apps/docs/src/content/docs/concepts/client-lifecycle.md": "fcbaca84f266060b946eed9cbf9d0b9815352b7b1d6ddaff5803b29484345380", - "apps/docs/src/content/docs/concepts/data-normalization.md": "f1505ec3eb35a33fac289706c2cc318a2ffb80555e3302ca102e67e21c9012da", - "apps/docs/src/content/docs/concepts/http-utility-tree.md": "ed03938167f31822b76efbeac381fd3f8d297061fe567928ee022f8dbfadb9b4", - "apps/docs/src/content/docs/concepts/queries-and-mutations.md": "204141ba9e3b52e8e0b2d994ef4381cfcecf898d0bd011866f8cbfc8e89cb15c", - "apps/docs/src/content/docs/concepts/rpc-utility-tree.md": "fd5fcc232279e9577b8b2faf321a85e9a340e6090ea5887bbab208cca2d24a4e", - "apps/docs/src/content/docs/concepts/semantic-keys.md": "4fcc7437b4c0698f590926545e41b68eed1094027fb18a8834fb695db63262c6", - "apps/docs/src/content/docs/examples.md": "4035adf9d277a7a3d75b22be8147ee6acb3329e6e96ca7c76b9d4fc052758239", - "apps/docs/src/content/docs/examples/cancellable-commands.md": "c175cbd7f15b2322b3039a95aba937a440ae4198794195b6db7419b2a3acb245", - "apps/docs/src/content/docs/getting-started/compatibility-and-stability.md": "74f9d59c908fe6ab70ccfb8940c1f976e060b38a05cc6bbe75f72403d3b3ed7b", - "apps/docs/src/content/docs/getting-started/feature-support.md": "5a6b57b8e539937b203da9bb571d31da2169a014f6f0ee6d0625627a8b0f7525", - "apps/docs/src/content/docs/getting-started/http-quick-start.md": "9df7c42c5f87270a065b4cfafb2f717c7735db79268fd8b9ed30b73b03fa9cc2", - "apps/docs/src/content/docs/getting-started/installation.md": "fde89cc5c10d005672db696bdc8016deb42d86977fca1b89cfe6315c760bf2d3", - "apps/docs/src/content/docs/getting-started/quick-start.md": "5d0b2469e2e960c87110e0bfe404ad4137f6df1362c1e72989417db87a97b6fb", - "apps/docs/src/content/docs/guides/angular-query.md": "82b6cc47488db0b166b0f79597526e26274823b48377482fb52949a00c5a9515", - "apps/docs/src/content/docs/guides/cache-filters.md": "ac56ebb6d8949bec515be80850a5386d79eb9dc38533286ad3fa638f82654418", + "apps/docs/src/content/docs/concepts/client-lifecycle.md": "10da4a8517faea055cea83f158bb68cf3c8c60b0a1fc4cb53bf9900a846db91f", + "apps/docs/src/content/docs/concepts/data-normalization.md": "c67ac43accbca0b1982da942a7e5bb32c68e7cdd0d94a6a7e4e8e74f15a8fba9", + "apps/docs/src/content/docs/concepts/http-utility-tree.md": "adb5974bce5b1061ef522018e11554b03b13ce8ecf82fdfa40a100f0f903ad22", + "apps/docs/src/content/docs/concepts/queries-and-mutations.md": "435f1b7eb715156aa838954da5e90ecdccbd81ca32af3a8c095dd6b0bcb74faf", + "apps/docs/src/content/docs/concepts/rpc-utility-tree.md": "507544a84c4e5159a69fcc74588bffb0e3fd9217ec5b71fe2cde1bd39c01d8b9", + "apps/docs/src/content/docs/concepts/semantic-keys.md": "f5c23587532f038e1f8933590fe747486c82e6c1da3c8ad747eb647c03bf9487", + "apps/docs/src/content/docs/examples.md": "6fccd10d78463e14df3300312fb4e8714b3c607649880b48d0e1514fc4c34408", + "apps/docs/src/content/docs/examples/cancellable-commands.md": "272c01ab8cc1b63b1d2c064fd5b97da7505fe721da07c9301a2e4f779ac8a73a", + "apps/docs/src/content/docs/getting-started/compatibility-and-stability.md": "dbb168ad0411e22d0989c7421ff9576762315c82ce270bf6b65e876d1ec96bdd", + "apps/docs/src/content/docs/getting-started/feature-support.md": "0c44d3f5dac0be81fb154e0663368795d7794faf07fee9e79c75844a23fa93f4", + "apps/docs/src/content/docs/getting-started/http-quick-start.md": "791c00beac0d39ef5f46f73e0af3033b29be7274f9a1cbfe39f781975ce7d7cf", + "apps/docs/src/content/docs/getting-started/installation.md": "2fb452a210dc1cb6488651a31442c8acde35e1b5ebde8253810dcf258e6dabe3", + "apps/docs/src/content/docs/getting-started/quick-start.md": "148f23bc4320388882b3bb9a473e27bdc510e12515f73ba2af56ca854444aa39", + "apps/docs/src/content/docs/guides/angular-query.md": "3d6ba5560db5c4f03969da124cce568ac883bd148ad5bafe5045d53f3059be51", + "apps/docs/src/content/docs/guides/cache-filters.md": "71ce56ff7f9804ea92e43fdd0c37928f4c0d7c74042eaacc08f0c034f689db2c", "apps/docs/src/content/docs/guides/cache-management.md": null, - "apps/docs/src/content/docs/guides/cancellation.md": "8566563c90d913c4db16435d314eba1ec53d6873b30aa5652097d0994c50cc4c", - "apps/docs/src/content/docs/guides/choose-stream-history.md": "6996df3a8b18c4fadbcc76a3635a8a5408f155b4068509d4801b38afa7f22f16", - "apps/docs/src/content/docs/guides/conditional-queries.md": "7cc52d18217653c63699e78ecce3efb78c484e57ba75a92071df5d16b9a40ac8", - "apps/docs/src/content/docs/guides/custom-key-encoders.md": "e48f1768db1c9e8ab3d5915593dc6de9089c35c039fb16e7ca04a4326a4df8bf", - "apps/docs/src/content/docs/guides/handle-failures.md": "7bf37fc98c4a4eab109ead80d24febe4dda968267f07e7f3d665297c746a9c2b", - "apps/docs/src/content/docs/guides/http-metadata.md": "b0b45e8c3af5ada0d64cf61bd5bca161186a604c30d3e9fc63ba9090c590e542", - "apps/docs/src/content/docs/guides/http-queries-and-mutations.md": "23edf1a1c71a29561d9c87cc7fca1f7c8ed370d6e62be5719dcf54d533c420b9", - "apps/docs/src/content/docs/guides/hydrate-query-views.md": "d71f573e4c6e6815e51f7a63080b773655f90cf9ef07155d293f1550eac091d4", - "apps/docs/src/content/docs/guides/hydrate-unary-data.md": "0cf27b204e709f8a753fd61fd1510073404611e53a9fd6e82e8122af7e68f2fa", - "apps/docs/src/content/docs/guides/mutations.md": "773d6720894a83083bcd54adb55b632a9902894165a7c147c1266620774d8f21", - "apps/docs/src/content/docs/guides/optimistic-writes.md": "8d5001c0c1caa2f4cc25ce10955791eaef2f032327fc9c8a743173ffd60b68b5", - "apps/docs/src/content/docs/guides/pagination.md": "95a82e2cc2a972080152ac0816e06262ad63c7c0a7f9c8a60b3f836a42120a1a", - "apps/docs/src/content/docs/guides/queries.md": "6fb845e9d9d86a94b4df323583c9f721c171d135e2cb64f184f8a6fea22350e7", - "apps/docs/src/content/docs/guides/query-defaults.md": "74b7f9cd1c29b6bfa3777e8b48cc3dc6319856b48029d052adb4de34a9f5ddaf", - "apps/docs/src/content/docs/guides/react-query.md": "c8b23f26fd836a719cd3a03dc53c9abf46b1e607383cf0f66a59355bb4953671", - "apps/docs/src/content/docs/guides/refresh-from-events.md": "a8e680ae9060a573336829b94dbf77dd32385671d48cd4aa0f092a1374444cd6", - "apps/docs/src/content/docs/guides/retry-queries.md": "4326d4e0cec0ca0067182020f68e4db0f634ecefedcc5cadd0db3e90e3709684", - "apps/docs/src/content/docs/guides/solid-query.md": "6ce866f49974edc7f5db579d08134734544de1137d5a9e5efdf49bc62d12b7f2", - "apps/docs/src/content/docs/guides/stream-snapshots.md": "91e22e85c11d8822e25e5abb7b872b78f07af79fdb16eb2690baf8226fdbd9f7", - "apps/docs/src/content/docs/guides/streaming.md": "2d43cc0ac6f007f31aabfe01200d895827cb14ec1166324eb9661d146cda240a", - "apps/docs/src/content/docs/guides/suspense-and-recovery.md": "a5f92087e5006d2162ab623cdc39d98a41e13b5967e7483e3db7fa46e436e730", - "apps/docs/src/content/docs/guides/svelte-query.md": "967257cd51f59723bd641c8553a4e50b931258e79f24d60f47fdfb3035ffe6a8", - "apps/docs/src/content/docs/guides/switch-cache-owners.md": "cc209f13becec305af60c9f04fe29f85a2ca6db3bc11601cf09083bc2583d198", - "apps/docs/src/content/docs/guides/tanstack-start.md": "7ec04e78ab8798f8cd89839f2a07b466f0c9834557126799373940b161a95b0b", - "apps/docs/src/content/docs/guides/trace-streams.md": "4213d016728cfc45a31f7d7cb125362f7a4edf46bf6e9c40b325642e800f5ba1", - "apps/docs/src/content/docs/guides/vue-query.md": "bd8b50c940d0493780a60ae0cd80fc2843b2ebb22c2d3c9bc70653e0379d4155", - "apps/docs/src/content/docs/guides/websocket-clients.md": "1cd31655e433ced7b24795255cb03280b27734eda58afeb9730a42bfad433c43", - "apps/docs/src/content/docs/index.mdx": "b5bcd9d26ee0b8ea181b0c063afbae9ee057e31db9a2b3252c13589b5847339a", - "apps/docs/src/content/docs/integrations/index.md": "7ee02cd3d7d1d212ac0e539dc6dde5bf928d906797abcac34da1e7ec1352785a", - "apps/docs/src/content/docs/reference/browser-and-worker-hosts.md": "70743a4429a39937515c8d5fee79ca75714a92dd526f1093f9fa8ea12fbde984", - "apps/docs/src/content/docs/reference/compatibility-and-limits.md": "4f60753887b52c33a47b993f0aaaf75832edaeb09d082f2664e2e5e512e568a3", - "apps/docs/src/content/docs/reference/errors.md": "57b90eca4db33880af66063e8211b4a39d0850af811beafc8711bb01860063d5", - "apps/docs/src/content/docs/reference/external-http-clients.md": "9bfef63f64b378847d99092f088492f989ba0577810e6aa07bb194d55ca5fcbb", - "apps/docs/src/content/docs/reference/factory.md": "fb8e57908ccb82d57b3b29664034b3a210945b9bd39e030cd9613015ea3b0c37", - "apps/docs/src/content/docs/reference/generated-builders.md": "0c0406824a2f906ec4d5601ab068d6c1d2b35ce8b9602618f992eff7de8824f2", - "apps/docs/src/content/docs/reference/http-builders.md": "153a63a31bdfaa8411a4af067d555e7fe1af037c9d2f798a7f7a21b4e4be0a86", - "apps/docs/src/content/docs/reference/http-factory.md": "d40fd0597a9d784e21bf7f80ec9cf56008511f472625e572ffded61db0972f68", - "apps/docs/src/content/docs/reference/performance.md": "2f68c31cae28de0a84b1ce77f065481d37899171b78d9593ff65e017ff75d97a", - "apps/docs/src/content/docs/reference/public-exports.md": "9550217e7724f7d15542ff83e0a268bfe7274f7af5a4c3440ef0ee418607a6d9", - "apps/docs/src/content/docs/reference/type-inference.md": "1edcf7de07709644784a1f7dcc5462f0eb1e01e168f30ba9b1db102ebd424fd6", - "docs/adr/0001-target-effect-4-release-candidate.md": "44119ba9104f767c5804c5026c4a21a78a8367f6d9ff5a9035bb3f423969fbb3", - "docs/adr/0002-let-call-sites-choose-query-or-mutation.md": "4244d48ff136eea562aa2f94a0b6b1ab58412796f3ece47a3cf5b43f746e11f5", - "docs/adr/0003-project-dotted-rpc-tags-into-utility-paths.md": "a99856e94af4004484fed939d46ae4388363031cf396b07f04a92a7119fec2fe", - "docs/adr/0004-build-core-shaped-options-and-certify-react-first.md": "0e47069738c3792430c92b396068ac269139356c83738453889517d97e22f190", - "docs/adr/0005-preserve-the-cause-in-one-error-wrapper.md": "1e04885542f22f166b5daa913ae2dae121c407baa775a2c4a16590f96857e3fe", - "docs/adr/0006-use-canonical-semantic-json-for-payload-keys.md": "694d8665938af25abb91ada71ea9c95cb77dd4ea90019adb2f642e16a88948c7", - "docs/adr/0007-keep-the-core-factory-lifecycle-neutral.md": "a83dc0d6061ac0328a3ee6075aa872a55feb4f0154edf6dae964e74276bb15b5", - "docs/adr/0008-use-flat-prefix-matchable-key-tuples.md": "7fb791e73e0266afd8583e45059aebcc10bbd9704d625bdefcafec1481a46cf6", - "docs/adr/0009-build-an-eager-frozen-utility-tree.md": "bc3b08a3385f1dc8874d81f867f15ef6c30b203c94936fd991d2941fd5bacc27", - "docs/adr/0010-reuse-tanstack-query-core-as-a-peer.md": "633a1ce44c8d58b29096f540871b61566352462e1d4bd5335162b3e08652527d", - "docs/adr/0012-normalize-undefined-query-success-to-null.md": "5d076f168223f67a205138d3e258f1f3aac9dfe5d607ffe8efd12a71322ef022", - "docs/adr/0014-require-query-stable-payload-construction.md": "2f4381fb3e7406304ff3b914db541419f799f8a71d1d527b37a9e971e9949e47", - "docs/adr/0015-centralize-the-toolchain-in-vite-plus.md": "d2001383bc0f92363289c04b6b8df26acb5b0f5dfc0163b2a838f2eee8057b75", - "docs/adr/0020-generate-accumulated-and-live-stream-queries.md": "5e4f5c92e08ffb769137e930908dded993f928a497b018f9c6e4fd4f12232727", - "docs/adr/0021-share-utility-construction-through-private-modules.md": "821720a3eee166d4b7d42b6f5c3906bb29dff339b4815e9ed1c516bc5af66fd4", - "docs/adr/0022-add-buffered-http-utilities-with-separate-key-roots.md": "f1fb5d91cd91a70b356bc98b84176920ec822315dbdeaf5e247921853598b62f", - "docs/adr/0023-expose-buffered-multipart-http-mutations.md": "b72f20cb513d2a7db94b2dbe27576352048ba22288ad6eac6e80d1a7c81cefb8", - "docs/adr/0024-project-http-streams-and-buffered-metadata.md": "2797a5a4a751c5e37c38aa454c14b99ce1615870ffa13cfcedb98cca8a0af633", - "docs/adr/0025-use-public-decoded-message-clients-for-server-local-rpc.md": "36df8e1227af22018d5a9063699986ceb8dcfd4c8bb5615aa62557f2ec36e111", - "docs/adr/0026-keep-rpc-transport-aggregation-in-the-application.md": "b73af29d8f73a36f61f110ad8778e045d37253fe42a8888883e78089b93e955e", + "apps/docs/src/content/docs/guides/cancellation.md": "f5a1e5863b8f173470ed78616ede0b8bfb27488bb60252ad48dffc622a339ccb", + "apps/docs/src/content/docs/guides/choose-stream-history.md": "c138808345ba4b1220ba24deef63150ad188747ade7213d7243d00d08ef22cf2", + "apps/docs/src/content/docs/guides/conditional-queries.md": "9b1d0fd9449bf92c500dd8b60d1a5a01e5675b0e223d3c789ca04a7150dfbcd6", + "apps/docs/src/content/docs/guides/custom-key-encoders.md": "8bdcb04d74913f0906069d301b0c477413925b66be5e28d139b35a9797216ec6", + "apps/docs/src/content/docs/guides/handle-failures.md": "633c88a58464798f5fb46d2059b63927cab2735aa552ac4b1d0b222579272ade", + "apps/docs/src/content/docs/guides/http-metadata.md": "c634f1c609500456551b48142396a9db1f7ad0e2c6a692c4dec547feb177ec1a", + "apps/docs/src/content/docs/guides/http-queries-and-mutations.md": "b5c646131aa5c3c641579c5e75963052ae942cac9a0b427f763108ef4d45e648", + "apps/docs/src/content/docs/guides/hydrate-query-views.md": "d69329e9636fdc0f5a4249e8804da8b15e5793c4fde00b2148b6419baf050cdc", + "apps/docs/src/content/docs/guides/hydrate-unary-data.md": "a935103d99fd3917392e24dec0bf3e53d118a5d4af6b966a9d8a8e876f76394c", + "apps/docs/src/content/docs/guides/mutations.md": "9126b133c1fb97d8ccf1a6151cc37c1677428b3668d0585588d416254c01daea", + "apps/docs/src/content/docs/guides/optimistic-writes.md": "d5c1cbca4f484c38c6c4fd86ec38a25c96da0a6320cf7dbe91eb24fe02179d88", + "apps/docs/src/content/docs/guides/pagination.md": "3f126136723ff6848a34d0392f3669d1190a884299256c3f1b13615028ec13f8", + "apps/docs/src/content/docs/guides/queries.md": "26f8f6ae6ea5e745c155400759faa5680b5735cfae9046e3e9edc1105c1fa5e6", + "apps/docs/src/content/docs/guides/query-defaults.md": "52afde31f49c855f9fc83a43d85747a052d9cbfbdb5fc5aec4a08aee9c92c40e", + "apps/docs/src/content/docs/guides/react-query.md": "b1cf8b84280357141df3b478fa45a2e322b8473f06b706199d389690a40bbb93", + "apps/docs/src/content/docs/guides/refresh-from-events.md": "6167c023679b311c6292466b8fe134bc5ab035ff7df55fe9e73c8658a1fe20b0", + "apps/docs/src/content/docs/guides/retry-queries.md": "a1ab846da36b796f695edb4b1a0033a508901feb128cab4a589b20f962a7bd4b", + "apps/docs/src/content/docs/guides/solid-query.md": "1d244991dfcecdc6605613f922e0a33bfa16dd2abfed7bd7a36f28b076626ea0", + "apps/docs/src/content/docs/guides/stream-snapshots.md": "1f397caa3bf6651b02f8c445bb34d0cf66f19020b70da0d353217e61163de756", + "apps/docs/src/content/docs/guides/streaming.md": "1d4915f53e1b1e3ffd5f997116efd03adc65a3481235fda346f87f5ae61f55a5", + "apps/docs/src/content/docs/guides/suspense-and-recovery.md": "2b86a4b9bae6e530c6ee096093673f2a204cb7ac86e956b1a217f0a888c5b161", + "apps/docs/src/content/docs/guides/svelte-query.md": "bd29928e2be9618aad82e70235358aa297bf132d345d6b796126a746015a6c73", + "apps/docs/src/content/docs/guides/switch-cache-owners.md": "18e4ce7507a1a3572612f9c7de282dac2a23507cf9111cdb7756923903e1674e", + "apps/docs/src/content/docs/guides/tanstack-start.md": "3a8a702708a4b1bd9b2a93f22d20e980d55fcd15d5c5a986033da1c5d542b5c8", + "apps/docs/src/content/docs/guides/trace-streams.md": "b90ed9a5922a1dfbce58e90e039e149cae9efd28c11a98e79f81203b0fc302c4", + "apps/docs/src/content/docs/guides/vue-query.md": "c89cb2205e7fed5a0e9b707a41b8dcebc3381cdae8a79a1be6dc6421a30ed39c", + "apps/docs/src/content/docs/guides/websocket-clients.md": "c247492f252393d7bc57a0e107b95619c6899b79d6e25ecd884b10cb3d8bd2a7", + "apps/docs/src/content/docs/index.mdx": "f67c034d8ba493f6ec6ca4e1268adff194180b2f975589dbb8b7d66b837e866d", + "apps/docs/src/content/docs/integrations/index.md": "a67e580f5d8113cde0d1ac422cbc1270a5c65c7d334a4d1acfbae77a733a6375", + "apps/docs/src/content/docs/reference/browser-and-worker-hosts.md": "bdb546e21f000f227c076bcd383a425a9480c3ee515f5a831fd9e7cc5e663699", + "apps/docs/src/content/docs/reference/compatibility-and-limits.md": "d921d2fe2578a8fa545ecb8bfa715b705db31db031b24222c6660df6983bd471", + "apps/docs/src/content/docs/reference/errors.md": "67df05b0ae18702347c23ad08458b970ddbbb27bdf2bc616d90ca96bb630815b", + "apps/docs/src/content/docs/reference/external-http-clients.md": "a03f55ea87c0b28beac94775aeaac8e21b6b63d2348cedb441b50c9a33c08a3d", + "apps/docs/src/content/docs/reference/factory.md": "359ec1488dc6d551d84413faaa016d24954473ccc0705a6ee988dc512e3c506b", + "apps/docs/src/content/docs/reference/generated-builders.md": "d1b39925363e7d13b815c3e5312692cbee2a0665ee8afc6b99362b2f33d3aa58", + "apps/docs/src/content/docs/reference/http-builders.md": "fedf31eb11b3532aee81955939b5a40e86a0dea58334a423c695963782b439b0", + "apps/docs/src/content/docs/reference/http-factory.md": "ad8d90aede30203ebbd7c9894f5896c8db748ed0b20b6e6943c61425c2e5b925", + "apps/docs/src/content/docs/reference/performance.md": "9fcd785bf8df6510e69541da71c2600cb7762899a886d258b1a99eb42dcfdb29", + "apps/docs/src/content/docs/reference/public-exports.md": "141219f987b5c33f6b1f0122063634c2cd2c8bba9f965cad88712cb097d617af", + "apps/docs/src/content/docs/reference/type-inference.md": "9b8208d0d6cb49894416bde7161ea74463f1d3e8fbb1d243ed1a11e2749e3ed0", + "docs/adr/0001-target-effect-4-release-candidate.md": "df4be0035618ca4b6ca5816cb41fdeba182279a07db35caa2b5a2d7128d7d786", + "docs/adr/0002-let-call-sites-choose-query-or-mutation.md": "4d8c0e6ac2d6aa68c9a295025463854771720b4217b9a0f8f832ec6f5813a090", + "docs/adr/0003-project-dotted-rpc-tags-into-utility-paths.md": "2cc3876c9b9d57e9ffee57e8a6c98a0519ef21d3674150b8af283aa167bf5458", + "docs/adr/0004-build-core-shaped-options-and-certify-react-first.md": "051ea72c3a257e16fecafbb4dc1970eaecaf921729e3ddfa843440368470ccee", + "docs/adr/0005-preserve-the-cause-in-one-error-wrapper.md": "3fafcc3240839d332ffeb8c948711b5d0f69aecd3c9209bcd63871390d382fbb", + "docs/adr/0006-use-canonical-semantic-json-for-payload-keys.md": "c34cb02991666a22cd1e89f068b15f8f95d31728e7bcaac2647417b7f00a8b2f", + "docs/adr/0007-keep-the-core-factory-lifecycle-neutral.md": "a07e900d13418d9d6ff11bba41649e44cc9e5a5cd1606d919e333075ed03163d", + "docs/adr/0008-use-flat-prefix-matchable-key-tuples.md": "981b875f0b90111382290be898ccac2df125a7b9ca9220c6a3f53e9f1b88a3b0", + "docs/adr/0009-build-an-eager-frozen-utility-tree.md": "dec654dd6e4e4e52867188f681e2df23f1c9f1fc00cde0f0cf9736ca27a683ac", + "docs/adr/0010-reuse-tanstack-query-core-as-a-peer.md": "fa4a4ee5204ad923395b578633b2982e586c2960bf733abbd18ab55bc84a8918", + "docs/adr/0012-normalize-undefined-query-success-to-null.md": "989af99cea4083fd55c13fa37e383f6f33f4929ba2f20aa68bf7c8a6d6885f44", + "docs/adr/0014-require-query-stable-payload-construction.md": "62ee6663061a60b23396345669d42514db8aa7bad3c1a3d4fbe8cd7e3230a9b8", + "docs/adr/0015-centralize-the-toolchain-in-vite-plus.md": "c9179f7fd8cd90fa2dc5e51eb95f60180f399fd6df3202f8020ab40352a1bff1", + "docs/adr/0020-generate-accumulated-and-live-stream-queries.md": "76788b314e904c911bb0814ea4a9bcc053faebcda134780f2b4d09fde83e7990", + "docs/adr/0021-share-utility-construction-through-private-modules.md": "5bde2e0f2b1dc53103eb21094b52f22757386413e25268066c84f27adcf7197f", + "docs/adr/0022-add-buffered-http-utilities-with-separate-key-roots.md": "3472a800096e8e60b5e1861305792ff2667ba84143ced53c03f23899dc76fdb3", + "docs/adr/0023-expose-buffered-multipart-http-mutations.md": "ac91d652dde8484f3603754d74bbfb882602d07759f24bea08b5ef723a2e677b", + "docs/adr/0024-project-http-streams-and-buffered-metadata.md": "f11636c13e4685c3728c11e6a659e27bce3655d8083d4e8c4c4eb5a63512d894", + "docs/adr/0025-use-public-decoded-message-clients-for-server-local-rpc.md": "272358c7168709ebb1e827d5ee370d6cb4613efca9f2f462aacc445f2e180d69", + "docs/adr/0026-keep-rpc-transport-aggregation-in-the-application.md": "e50b09a3d14337e3a1e3e2374fa1de162e6b787431bca698eaffba425334e9a0", "examples/server/src/commands.ts": "58f415fe0ab9bb0127c96c11ef0540a29b97d231eebd3a487513f0820a46b3d5", "examples/server/src/diagnostic-operations.ts": "ddf8a0f2367b91ca08d6aaaf58380a51b77a5c51a9a85412bd7e7080ff0453ee", "examples/server/src/domain.ts": "c00fced49e54d7bfff9045479e91aabdbd7c0868cc89afe00bae5473b17d2f82", @@ -388,13 +388,13 @@ "scripts/measure-construction.mts": "10522cb10ab51fd44d8990896a2a73e8bf4d4cd2646441e958b6180cb1afd147", "scripts/verify-docs-examples.mts": "2fac318ffe8e627d0d19c6434171444647f1bc1369024501a3091cdefe06ed72", "scripts/verify-packed-consumer.mts": null, - "skills/effect-api-query/SKILL.md": "41c0a5768977c0e25254f559de4454df7d9dc5fb6075487d65a6a52602b0e7d4", - "skills/effect-api-query/references/cache-workflows.md": "61d3d363b410eac3f52273e642ec2924d29e887f365a7c21a965a52deeded0be", - "skills/effect-api-query/references/frameworks-and-hosts.md": "e84b9179ca0cba6694200f504923906d78b3b7a305315f5d1453dace854aefb5", - "skills/effect-api-query/references/http.md": "4dd64a0fcc036f3ee857ae96d0e0149c729e0ea95dfd79a82a62c76db4619999", - "skills/effect-api-query/references/hydration-and-ssr.md": "1a6ba66187a1c786c09eb08dbd1c0f0e7c04e21d3b4787eb534687a67c1a38f0", - "skills/effect-api-query/references/query-patterns.md": "2b9ff5a7f9857d00dee85842351bc7ff8d3e88a016e1c0056c2ddb513cf53a64", - "skills/effect-api-query/references/rpc.md": "2d2d30f309424f255e3162fd52dbdaf6d3883e40d9eb6f1840eb814424b0abf3", + "skills/effect-api-query/SKILL.md": "fc5169c63789f37e6d0c674f22bf8620759b47bfa85ba2448c6582c03f5f6a1a", + "skills/effect-api-query/references/cache-workflows.md": "2b54d5ee96eb66a73eaa2896dec9ca04cd08b9bb21d7aea692eb99a6b8c0e0ac", + "skills/effect-api-query/references/frameworks-and-hosts.md": "057bee8c6563e086b9fa9580605f6cae79e9ee5d272b18a71841bbe87c0d776f", + "skills/effect-api-query/references/http.md": "e0d7071c1244a2b0ca9bac9ff12f73863763197e3d67a37ca62c96be572f5bcb", + "skills/effect-api-query/references/hydration-and-ssr.md": "2221298e3cb3f029a04f9ebf91254532eb8f8f2e95d0bb368221e055d180bbf1", + "skills/effect-api-query/references/query-patterns.md": "67e42a182c467b5a09cdff139808e6b7f6bbf280e487e59f24b7f1e4d52d7d69", + "skills/effect-api-query/references/rpc.md": "d02e4d75559a1e3d1998ebefc657d3e6d070201715d50352d25f6b6ba542a567", "src/core/inference.ts": "b8345c0961c67ceb41ee18e017302a5ef5d6be91794b861e72bece21ee5e9c30", "src/core/operation.ts": "7dabe4d21070d20534643e5051060c84a09db5a7aa39858e75846043f8a537ea", "src/core/policy.ts": "bebe7a32bb958ba9a1817c6fec6792fd04541f5ae81cd70fa422106ac3cb4073", @@ -485,16 +485,17 @@ "tests/types/tanstack-start-contract.ts": "51681e3d0d7504a614d490f18adb6f44db82d34b9d4d7cb9cf941aea33de284f", "tests/types/type-scale.ts": "a9b307c25c72f3c184eabc8e22ddaeea96feb331c0f8d748773d765d358dbfca" }, - "head": "cb14a01c995cd9dfac5a3c5dc752ea5d71da862f", - "outcome": "no-change", - "reason": "All three planning records remain accurate: domain_map.yaml covers the same contract, provider and tracing tasks; skill_spec.md retains the same ownership and executable checks, whose modules now import their extracted parts; skill_tree.yaml already maps all canonical guides and tests/**/*.ts and tests/**/*.tsx. Deployment/version-label maintenance introduces no consumer skill task, new failure mode, identity or source coverage. Preserve the accurate records without a no-op history entry.", + "head": "0feea99e2469c98d60567fef8e0e7baf26f69625", + "outcome": "updated", + "reason": "Reconciled all three planning records. domain_map.yaml prose is clearer while its fourteen tasks, ten failure modes, four tensions, priorities, sources, and examples are preserved. skill_spec.md is rewritten with a source-bound editorial batch, scope exclusions, agent-facing guidance, checks, and unchanged consumer/discovery limitations. skill_tree.yaml is a verified no-op: identities, activation metadata, six-reference structure, dependencies, source mappings, and distribution remain accurate; sync reported no changes.", "evidence": [ - "Source cb14a01c995cd9dfac5a3c5dc752ea5d71da862f plus current local diff. No public library behavior, types, dependencies, package version, or publishing setup changes.", - "Fresh vp run --no-cache api-types passed package/declaration construction, attw, publint, and actual importing compiler fixtures under TypeScript 7 and 5.9.3. Exact documentation excerpt verification and Astro checks passed; docs-build produced 56 pages with valid internal links.", - "Type-aware lint and checking passed 150 files; repository formatting passed 310 files. The two existing React mounting and stream tracing runtime recipe tests passed after real module extraction.", - "Seven Chromium documentation checks passed against the fresh local build: navigation/search/assets, published registry version, unpublished/network/invalid-version fallbacks, and generated LLM documentation links. Collaborative browser inspection confirmed separate quick-start and React modules, preserved React anchor, and the correct October 11 page-change date.", - "Workflow YAML 1.2 parsing and dependency/branch/artifact inspection passed. Documentation now depends on successful validation on main, reuses its browser-tested artifact, and has no publish dependency; standalone manual main builds check compiler fixtures. No live workflow was dispatched and npm publication remains unconfigured as confirmed by the maintainer.", - "Intent sync reported Nothing to synchronize. Reviewed the owned skill, RPC/HTTP and framework references, and all three planning records against the actual diff. Existing source patterns cover every new fixture and changed canonical guide. Fresh consumer/discovery sessions were not repeated because recommendations and library behavior are unchanged." + "Source HEAD 0feea99e2469c98d60567fef8e0e7baf26f69625, package effect-api-query 0.1.0, and actual uncommitted prose diff. Runtime source, public types, dependencies, examples, and activation metadata are unchanged.", + "Protected-content audit: all 92 in-scope Markdown/MDX documents revised; all 113 code blocks, Markdown link targets, and heading anchors preserved. No prose em/en dashes or semicolons remain. .agents/, docs/agents/, and AGENTS.md match HEAD.", + "vp run --no-cache api-types passed package build, attw, publint, and public API/documentation fixtures under TypeScript 7 and 5.9.", + "vp run --no-cache docs-build passed exact documentation excerpt verification and Astro checks, built 56 pages, and validated all internal links.", + "vp run --no-cache format, git diff --check, and vp exec intent validate skills --check passed. Intent maintainer sync reported nothing to synchronize.", + "Independent prose reviews covered technical conditions, cleanup ordering, key identity, shared callbacks, hydration, and agent-facing ADR/skill instructions. No runtime or recommended API behavior changed. A fresh consumer session and new discovery check were not repeated for this editorial revision.", + "Both planning YAML files parse with the installed YAML parser. Compared task coverage, failure examples, source paths, priorities, statuses, and complete skill-tree values against HEAD. All retained entries match." ] }, "source:vite.config.ts": { @@ -1057,31 +1058,39 @@ ] }, "source:docs/adr/0016-automate-releases-with-changesets.md": { - "fingerprint": "6ef98996d907f829fdbb01507863ffb03582e7a4ce75f2739bb5a29f2fac03bf", + "fingerprint": "99e44eddadc67ecda0235acf591328ed3264f0aa7f2b1346a8d8dc79fcebf1df", "snapshot": { - "docs/adr/0016-automate-releases-with-changesets.md": "35b5cdc29855b561c502d4bc5de33bbbbdd757ff69be55f13808d24176d5c67e" + "docs/adr/0016-automate-releases-with-changesets.md": "7a92886b85012f8358538e76ed0bf27c2a2e2004f7cf7f9791e786341c8edb6c" }, - "head": "6893603eab70bd9d67e9848414eccc8a3421d870", - "outcome": "no-change", - "reason": "The ADR distinguishes initial unpublished candidate preparation from post-publication Changesets and preserves main-only trusted publishing. This release decision changes neither public integrations nor the existing framework/host and runtime ownership guidance.", + "head": "0feea99e2469c98d60567fef8e0e7baf26f69625", + "outcome": "updated", + "reason": "Rewrote agent-facing release decision prose while retaining Changesets, review-controlled versioning, and release automation boundaries. No publishing workflow or library guidance change.", "evidence": [ - "Reviewed the complete ADR diff from 5b91ddb to 6893603.", - "Release run 38085762195 validated and packed the exact 6893603 candidate; publishing and deployment were skipped as intended.", - "Current batch: scoped vp run format, native vp run skills-check, and installed YAML parsing passed; vp exec intent maintainer sync reported nothing to synchronize." + "Source HEAD 0feea99e2469c98d60567fef8e0e7baf26f69625, package effect-api-query 0.1.0, and actual uncommitted prose diff. Runtime source, public types, dependencies, examples, and activation metadata are unchanged.", + "Protected-content audit: all 92 in-scope Markdown/MDX documents revised; all 113 code blocks, Markdown link targets, and heading anchors preserved. No prose em/en dashes or semicolons remain. .agents/, docs/agents/, and AGENTS.md match HEAD.", + "vp run --no-cache api-types passed package build, attw, publint, and public API/documentation fixtures under TypeScript 7 and 5.9.", + "vp run --no-cache docs-build passed exact documentation excerpt verification and Astro checks, built 56 pages, and validated all internal links.", + "vp run --no-cache format, git diff --check, and vp exec intent validate skills --check passed. Intent maintainer sync reported nothing to synchronize.", + "Independent prose reviews covered technical conditions, cleanup ordering, key identity, shared callbacks, hydration, and agent-facing ADR/skill instructions. No runtime or recommended API behavior changed. A fresh consumer session and new discovery check were not repeated for this editorial revision.", + "Reviewed git diff HEAD -- docs/adr/0016-automate-releases-with-changesets.md and retained the original decision/policy facts." ] }, "source:docs/adr/0017-quarantine-new-dependency-releases-for-seven-days.md": { - "fingerprint": "4be5f1ac6db00f4e928006c3e6230947279e5df2ecc5203eb48d4c96a490607a", + "fingerprint": "1e35fd9426bd8e97df3425cea88853271ff33c13320ce7c549988b43d75523a3", "snapshot": { - "docs/adr/0017-quarantine-new-dependency-releases-for-seven-days.md": "683309834b8c9e15b801ff9a7e640a6facf47a26690765deaf7a5e0a540dcef6" + "docs/adr/0017-quarantine-new-dependency-releases-for-seven-days.md": "e11273a9dcfac82d4c286c74bc7698b4d01c519504ba013ee54ec2870a7f926c" }, - "head": "6893603eab70bd9d67e9848414eccc8a3421d870", - "outcome": "no-change", - "reason": "The ADR makes missing publication times fail closed and applies the same strict age policy to isolated consumers and frozen installs. Existing consumer API guidance and qualified dependency versions remain accurate; no new integration task is introduced.", + "head": "0feea99e2469c98d60567fef8e0e7baf26f69625", + "outcome": "updated", + "reason": "Rewrote agent-facing dependency-policy prose while preserving the seven-day quarantine decision and its stated consequences. No policy, dependency, or public usage change.", "evidence": [ - "Reviewed the complete ADR diff from 5b91ddb to 6893603; pnpm-workspace.yaml policy remains unchanged.", - "Native eligible Intent 0.5.3 upgrade and isolated Skills CLI 1.7.0 install succeeded under minimumReleaseAge:10080, Strict:true, IgnoreMissingTime:false, trustLockfile:false without exceptions.", - "Current batch: scoped vp run format, native vp run skills-check, and installed YAML parsing passed; vp exec intent maintainer sync reported nothing to synchronize." + "Source HEAD 0feea99e2469c98d60567fef8e0e7baf26f69625, package effect-api-query 0.1.0, and actual uncommitted prose diff. Runtime source, public types, dependencies, examples, and activation metadata are unchanged.", + "Protected-content audit: all 92 in-scope Markdown/MDX documents revised; all 113 code blocks, Markdown link targets, and heading anchors preserved. No prose em/en dashes or semicolons remain. .agents/, docs/agents/, and AGENTS.md match HEAD.", + "vp run --no-cache api-types passed package build, attw, publint, and public API/documentation fixtures under TypeScript 7 and 5.9.", + "vp run --no-cache docs-build passed exact documentation excerpt verification and Astro checks, built 56 pages, and validated all internal links.", + "vp run --no-cache format, git diff --check, and vp exec intent validate skills --check passed. Intent maintainer sync reported nothing to synchronize.", + "Independent prose reviews covered technical conditions, cleanup ordering, key identity, shared callbacks, hydration, and agent-facing ADR/skill instructions. No runtime or recommended API behavior changed. A fresh consumer session and new discovery check were not repeated for this editorial revision.", + "Reviewed git diff HEAD -- docs/adr/0017-quarantine-new-dependency-releases-for-seven-days.md and retained the original decision/policy facts." ] }, "source:e2e/tanstack-start.spec.ts": { @@ -1283,16 +1292,21 @@ ] }, "source:docs/adr/0013-use-a-vite-plus-workspace-with-a-root-package.md": { - "fingerprint": "c18a01558cba99c73dd6bbc8381e3243a4a2f324069d47f6217c11a424cd615e", + "fingerprint": "cf12cc4f4d496c1e1afb2a852c1bb4c8105123c39fe5dec0c7eb80091638c1c8", "snapshot": { - "docs/adr/0013-use-a-vite-plus-workspace-with-a-root-package.md": "7617802c14fd07b0e147f8168381285b078eb752fbf9981848cbfe86e1f7792a" + "docs/adr/0013-use-a-vite-plus-workspace-with-a-root-package.md": "f1e7e8c340b0ce74e73ae931451cb7e825fafced4900322fe28ad9371205d1fa" }, - "head": "54b6521f3e37f9cb3ca4cd3812ab3c960eb2168a", - "outcome": "no-change", - "reason": "Removed obsolete beta and editor-detail wording while preserving the native Vite+ workspace decision; no consumer instruction changes.", + "head": "0feea99e2469c98d60567fef8e0e7baf26f69625", + "outcome": "updated", + "reason": "Rewrote agent-facing decision prose while retaining the Vite+ workspace and root-package ownership model. No workspace, packaging, or supported usage change.", "evidence": [ - "Reviewed the actual diff and current source at docs/adr/0013-use-a-vite-plus-workspace-with-a-root-package.md against 54b6521 and the supplied PR base; relevant native task receipts are under /tmp/effect-api-query-audit-2026-10-11/.", - "Final uncached native tasks passed: typecheck (142 files), Effect diagnostics (162 files, zero errors/warnings), Fallow, test (41 files / 474 runtime cases), public API and server-local fixtures with TypeScript 7.0.2 and 5.9.3, native pack with attw/publint, both example builds, docs-check and 46-page docs-build with internal links. Chromium passed all 31 application and 7 documentation checks. Firefox/WebKit browser launches are unavailable locally because their installed binaries are missing; their HTTP-only documentation checks passed. Fresh installed-consumer activation/task evaluation was not performed; compiler and application evidence comes from this workspace." + "Source HEAD 0feea99e2469c98d60567fef8e0e7baf26f69625, package effect-api-query 0.1.0, and actual uncommitted prose diff. Runtime source, public types, dependencies, examples, and activation metadata are unchanged.", + "Protected-content audit: all 92 in-scope Markdown/MDX documents revised; all 113 code blocks, Markdown link targets, and heading anchors preserved. No prose em/en dashes or semicolons remain. .agents/, docs/agents/, and AGENTS.md match HEAD.", + "vp run --no-cache api-types passed package build, attw, publint, and public API/documentation fixtures under TypeScript 7 and 5.9.", + "vp run --no-cache docs-build passed exact documentation excerpt verification and Astro checks, built 56 pages, and validated all internal links.", + "vp run --no-cache format, git diff --check, and vp exec intent validate skills --check passed. Intent maintainer sync reported nothing to synchronize.", + "Independent prose reviews covered technical conditions, cleanup ordering, key identity, shared callbacks, hydration, and agent-facing ADR/skill instructions. No runtime or recommended API behavior changed. A fresh consumer session and new discovery check were not repeated for this editorial revision.", + "Reviewed git diff HEAD -- docs/adr/0013-use-a-vite-plus-workspace-with-a-root-package.md and retained the original decision/policy facts." ] }, "source:e2e-docs/docs.spec.ts": { @@ -1624,6 +1638,78 @@ "The native task supervisor sometimes fails with spawn EBUSY or sandbox process initialization errors; identical installed commands ran directly after completing their package prerequisites. No checks or source rules were bypassed to obtain these results.", "No shipped guidance, activation description or public interface changed. Fresh consumer and discovery sessions were not repeated for this internal refactor; executable published-contract and rendered-application checks provide this batch evidence." ] + }, + "source:.changeset/README.md": { + "fingerprint": "44eb2527578c9cf4989dc93ae235e91e8c56abbc8194d362caed42190ad9ac86", + "snapshot": { + ".changeset/README.md": "e5d46bbaa2e9e5c229c16f5be8c327b6b19bfd6c35948c573480152b3e49a243" + }, + "head": "0feea99e2469c98d60567fef8e0e7baf26f69625", + "outcome": "updated", + "reason": "Rewrote release guidance in plain English. Retained the non-empty Changeset requirement for runtime behavior, types, exports, peer/runtime dependency ranges, and installation metadata; retained exemptions and root-only publishing. No new library skill or source mapping is needed.", + "evidence": [ + "Source HEAD 0feea99e2469c98d60567fef8e0e7baf26f69625, package effect-api-query 0.1.0, and actual uncommitted prose diff. Runtime source, public types, dependencies, examples, and activation metadata are unchanged.", + "Protected-content audit: all 92 in-scope Markdown/MDX documents revised; all 113 code blocks, Markdown link targets, and heading anchors preserved. No prose em/en dashes or semicolons remain. .agents/, docs/agents/, and AGENTS.md match HEAD.", + "vp run --no-cache api-types passed package build, attw, publint, and public API/documentation fixtures under TypeScript 7 and 5.9.", + "vp run --no-cache docs-build passed exact documentation excerpt verification and Astro checks, built 56 pages, and validated all internal links.", + "vp run --no-cache format, git diff --check, and vp exec intent validate skills --check passed. Intent maintainer sync reported nothing to synchronize.", + "Independent prose reviews covered technical conditions, cleanup ordering, key identity, shared callbacks, hydration, and agent-facing ADR/skill instructions. No runtime or recommended API behavior changed. A fresh consumer session and new discovery check were not repeated for this editorial revision.", + "Reviewed git diff HEAD -- .changeset/README.md and retained the original decision/policy facts." + ] + }, + "source:docs/adr/0011-publish-esm-only.md": { + "fingerprint": "d3fd2442fce92c8c4c7fb2f9467cccfe2b3da631768e94b37dd78845915e37d9", + "snapshot": { + "docs/adr/0011-publish-esm-only.md": "1b7237e42710b643dd6616409cd72ef938819a1d5f71e932ee4b9947b32bb9a4" + }, + "head": "0feea99e2469c98d60567fef8e0e7baf26f69625", + "outcome": "updated", + "reason": "Rewrote agent-facing decision prose while retaining the ESM-only decision, supported runtime/compiler boundary, and application responsibility for transforming dependencies. No runtime or consumer recommendation change.", + "evidence": [ + "Source HEAD 0feea99e2469c98d60567fef8e0e7baf26f69625, package effect-api-query 0.1.0, and actual uncommitted prose diff. Runtime source, public types, dependencies, examples, and activation metadata are unchanged.", + "Protected-content audit: all 92 in-scope Markdown/MDX documents revised; all 113 code blocks, Markdown link targets, and heading anchors preserved. No prose em/en dashes or semicolons remain. .agents/, docs/agents/, and AGENTS.md match HEAD.", + "vp run --no-cache api-types passed package build, attw, publint, and public API/documentation fixtures under TypeScript 7 and 5.9.", + "vp run --no-cache docs-build passed exact documentation excerpt verification and Astro checks, built 56 pages, and validated all internal links.", + "vp run --no-cache format, git diff --check, and vp exec intent validate skills --check passed. Intent maintainer sync reported nothing to synchronize.", + "Independent prose reviews covered technical conditions, cleanup ordering, key identity, shared callbacks, hydration, and agent-facing ADR/skill instructions. No runtime or recommended API behavior changed. A fresh consumer session and new discovery check were not repeated for this editorial revision.", + "Reviewed git diff HEAD -- docs/adr/0011-publish-esm-only.md and retained the original decision/policy facts." + ] + }, + "source:docs/adr/0018-add-infinite-query-builders-for-unary-rpcs.md": { + "fingerprint": "8614969c976daf63bb254b46c82bb81b4e5e48c72dcd3a1122906a2f8cdb78bf", + "snapshot": { + "docs/adr/0018-add-infinite-query-builders-for-unary-rpcs.md": "fbd51214b36a9fca8f9a7327be6fbe83662af16d1eb1ed4581b1123e8f3dbf86" + }, + "head": "0feea99e2469c98d60567fef8e0e7baf26f69625", + "outcome": "updated", + "reason": "Rewrote agent-facing pagination decision prose while preserving unary-only infinite builders, page-to-input mapping, base identity, and error semantics. No API or recommended pagination change.", + "evidence": [ + "Source HEAD 0feea99e2469c98d60567fef8e0e7baf26f69625, package effect-api-query 0.1.0, and actual uncommitted prose diff. Runtime source, public types, dependencies, examples, and activation metadata are unchanged.", + "Protected-content audit: all 92 in-scope Markdown/MDX documents revised; all 113 code blocks, Markdown link targets, and heading anchors preserved. No prose em/en dashes or semicolons remain. .agents/, docs/agents/, and AGENTS.md match HEAD.", + "vp run --no-cache api-types passed package build, attw, publint, and public API/documentation fixtures under TypeScript 7 and 5.9.", + "vp run --no-cache docs-build passed exact documentation excerpt verification and Astro checks, built 56 pages, and validated all internal links.", + "vp run --no-cache format, git diff --check, and vp exec intent validate skills --check passed. Intent maintainer sync reported nothing to synchronize.", + "Independent prose reviews covered technical conditions, cleanup ordering, key identity, shared callbacks, hydration, and agent-facing ADR/skill instructions. No runtime or recommended API behavior changed. A fresh consumer session and new discovery check were not repeated for this editorial revision.", + "Reviewed git diff HEAD -- docs/adr/0018-add-infinite-query-builders-for-unary-rpcs.md and retained the original decision/policy facts." + ] + }, + "source:docs/adr/0019-host-effect-rpc-inside-tanstack-start.md": { + "fingerprint": "1774ef38bbb6162ef15be7fce5dbba4ba781ba8fcf8b746cdf4adf0c41d54550", + "snapshot": { + "docs/adr/0019-host-effect-rpc-inside-tanstack-start.md": "dc498450821a420811533705e405d3ae8d6b5cd167e752452b6c2e721632b32b" + }, + "head": "0feea99e2469c98d60567fef8e0e7baf26f69625", + "outcome": "updated", + "reason": "Rewrote agent-facing hosting decision prose while preserving TanStack Start hosting and application-owned request/browser resources. No host implementation, lifetime, or client-construction change.", + "evidence": [ + "Source HEAD 0feea99e2469c98d60567fef8e0e7baf26f69625, package effect-api-query 0.1.0, and actual uncommitted prose diff. Runtime source, public types, dependencies, examples, and activation metadata are unchanged.", + "Protected-content audit: all 92 in-scope Markdown/MDX documents revised; all 113 code blocks, Markdown link targets, and heading anchors preserved. No prose em/en dashes or semicolons remain. .agents/, docs/agents/, and AGENTS.md match HEAD.", + "vp run --no-cache api-types passed package build, attw, publint, and public API/documentation fixtures under TypeScript 7 and 5.9.", + "vp run --no-cache docs-build passed exact documentation excerpt verification and Astro checks, built 56 pages, and validated all internal links.", + "vp run --no-cache format, git diff --check, and vp exec intent validate skills --check passed. Intent maintainer sync reported nothing to synchronize.", + "Independent prose reviews covered technical conditions, cleanup ordering, key identity, shared callbacks, hydration, and agent-facing ADR/skill instructions. No runtime or recommended API behavior changed. A fresh consumer session and new discovery check were not repeated for this editorial revision.", + "Reviewed git diff HEAD -- docs/adr/0019-host-effect-rpc-inside-tanstack-start.md and retained the original decision/policy facts." + ] } } } diff --git a/GLOSSARY.md b/GLOSSARY.md index a273b0b..1dc7ef8 100644 --- a/GLOSSARY.md +++ b/GLOSSARY.md @@ -1,79 +1,79 @@ # Effect API Query -This glossary defines the project-specific language for deriving TanStack Query utilities from Effect RPC and HTTP contracts. +These terms describe how the package builds TanStack Query utilities from Effect RPC and HTTP contracts. ## Generated API **RPC utility tree**: -A nested object derived from literal Effect RPC tags. Branches represent RPC namespaces; unary and streaming leaves provide their typed key and option builders. +A nested object built from literal Effect RPC tags. Branches represent RPC namespaces. Unary and streaming leaves provide typed key and option builders. _Avoid_: Router, generated client **HTTP utility tree**: -A nested object derived from an Effect HttpApi's literal group and endpoint identifiers. Supported buffered and SSE endpoints provide typed key and option builders, with multipart endpoints limited to buffered mutations; endpoints in top-level groups appear at the root. +A nested object built from the literal group and endpoint identifiers in an Effect HttpApi. Supported buffered and SSE endpoints provide typed key and option builders. Multipart endpoints support buffered mutations only. Endpoints in top-level groups appear at the root. _Avoid_: RPC utility tree, generated client **Query data**: -The successful decoded RPC or HTTP value presented to TanStack Query. Buffered queries and live queries convert successful `undefined` to `null`; accumulated stream elements and mutation data remain unchanged. +The successful decoded RPC or HTTP value that TanStack Query receives. Buffered queries and live queries convert successful `undefined` to `null`. Accumulated stream elements and mutation data keep their original values. _Avoid_: RPC success value when it is `undefined` **Accumulated streamed query**: -A streaming RPC or HTTP SSE view that caches emitted values in order, optionally retaining only a bounded recent history. Refetches may reset, append to, or replace the cached sequence. +A streaming RPC or HTTP SSE view that caches emitted values in order. It can limit the cache to a recent history. Refetches can reset, append to, or replace the cached sequence. _Avoid_: Live query, infinite query **Live query**: -A streaming RPC or HTTP SSE view that caches only the latest emitted value, converting `undefined` to `null`. Stream completion preserves that value; completion before the first emission produces a package error. +A streaming RPC or HTTP SSE view that caches only the latest emitted value. It converts `undefined` to `null`. Stream completion preserves the latest value. Completion before the first emission produces a package error. _Avoid_: Accumulated streamed query, subscription **HTTP metadata view**: -A buffered HTTP query view containing decoded query data and an immutable snapshot of the response status and raw string headers. Declared decoded header wrappers remain part of its data. +A buffered HTTP query view with decoded query data and an immutable snapshot of the response status and raw string headers. Declared decoded header wrappers remain part of the data. _Avoid_: Raw response, header-wrapped success ## Payloads and keys **HTTP request input**: -The decoded request parts accepted by a ready HTTP API client: declared params, query, headers, and payload. It excludes response controls; RPC payload constructors do not apply to it. +The decoded request parts that a ready HTTP API client accepts: declared params, query, headers, and payload. Response controls are excluded. RPC payload constructors do not apply to HTTP request input. _Avoid_: Payload constructor input, wire request **Payload constructor input**: -The call-site value accepted by an RPC payload Schema constructor. It may omit fields supplied by constructor defaults. +The value that a call site passes to an RPC payload Schema constructor. It can omit fields that constructor defaults supply. _Avoid_: Normalized payload, encoded payload **Normalized payload**: -The payload Schema's decoded `Type` after the constructor applies defaults and validates the input. +The payload Schema's decoded `Type` after its constructor applies defaults and validates the input. _Avoid_: Payload constructor input, encoded payload **Query-stable payload Schema**: -An RPC payload Schema whose normalized value can pass through its constructor again without changing its encoded meaning. +An RPC payload Schema whose constructor accepts its normalized value again without changing the value's encoded meaning. _Avoid_: Deterministic encoder, universally idempotent Schema **Canonical key payload**: -The immutable, JSON-safe representation of a normalized payload used for cache identity. It is independent of protocol-specific transport encoding. +The immutable, JSON-safe representation of a normalized payload that identifies a cache entry. It is independent of the transport encoding for a protocol. _Avoid_: Wire payload, raw input **Key prefix**: -A caller-supplied, non-empty tuple of JSON-safe values that namespaces a utility tree. Generated roots append an adapter discriminator and, for HTTP, the API identifier. +A non-empty tuple of JSON-safe values that the caller supplies to namespace a utility tree. Generated roots append an adapter discriminator. HTTP roots also append the API identifier. _Avoid_: Automatic namespace, client identity ## Execution and failures **Cancellable command**: -An application operation identified before work starts, with observable progress and an explicit cancellation request. Its terminal domain state is independent of the TanStack mutation state; cancellation stops future work without undoing completed work. +An application operation with an identifier assigned before work starts, observable progress, and an explicit cancellation request. Its terminal domain state is independent of the TanStack mutation state. Cancellation stops future work without undoing completed work. _Avoid_: Mutation cancellation, rollback **Ready RPC client**: -A flat, tag-first Effect RPC client acquired within a Scope that remains alive while an RPC utility tree uses it. The caller owns the Scope and disposal. +A flat, tag-first Effect RPC client acquired within a Scope. The Scope stays alive while an RPC utility tree uses the client. The caller owns the Scope and disposal. _Avoid_: Client factory, owned client **Ready HTTP API client**: -An acquired Effect HttpApiClient whose transport, middleware, runtime, and resource lifetime belong to the caller. +An acquired Effect HttpApiClient. The caller owns its transport, middleware, runtime, and resource lifetime. _Avoid_: URL client, client factory, owned client **RPC execution error**: -An `Error` produced when an RPC Effect returns a failed `Exit`. It identifies the RPC and operation and preserves the complete Effect `Cause` without retaining the input. +An `Error` that the package produces when an RPC Effect returns a failed `Exit`. It identifies the RPC and operation. It preserves the complete Effect `Cause` without retaining the input. _Avoid_: Catch-all adapter error, configuration error **HTTP execution error**: -An `Error` produced when an HTTP Effect returns a failed `Exit`. It identifies the declared API, group, endpoint, method, and operation and preserves the complete Effect `Cause`. +An `Error` that the package produces when an HTTP Effect returns a failed `Exit`. It identifies the declared API, group, endpoint, method, and operation. It preserves the complete Effect `Cause`. _Avoid_: Sanitized Cause, configuration error **Configuration error**: @@ -81,5 +81,5 @@ An error that rejects invalid utility configuration during preparation or execut _Avoid_: RPC execution error **Key-generation error**: -A synchronous error produced while deriving canonical cache identity from RPC payload constructor input or HTTP request input. +A synchronous error that occurs while the package derives canonical cache identity from RPC payload constructor input or HTTP request input. _Avoid_: RPC execution error, configuration error diff --git a/README.md b/README.md index 39975d5..9b9308e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # effect-api-query -Derive typed TanStack Query options and cache keys from your Effect RPC and HttpApi contracts. -Pass the generated options to QueryClient or your framework's Query hooks. Inputs and results -follow the contract; your application controls transport, client lifetime, and cache policy. +`effect-api-query` builds typed TanStack Query options and cache keys from Effect RPC and HttpApi contracts. +Pass these options to QueryClient or your framework's Query hooks. The contract defines the inputs and results. +Your application controls transport, client lifetime, and cache policy. ## Install @@ -12,19 +12,19 @@ Install the package and TanStack Query Core: pnpm add effect-api-query @tanstack/query-core ``` -The package pins one exact Effect 4 release because its RPC and HTTP API modules are unstable. -With automatic peer installation enabled, pnpm installs that version. Existing applications must -match it. Use TypeScript 5.9 or newer with `strict: true`, ESM, and ES2022 support. See +The package requires one exact Effect 4 release because its RPC and HTTP API modules are unstable. +If automatic peer installation is enabled, pnpm installs that version. Existing applications must +use the same version. Use TypeScript 5.9 or newer with `strict: true`, ESM, and ES2022 support. See [compatibility and stability](https://ueberbrot.github.io/effect-api-query/getting-started/compatibility-and-stability/). ## RPC -Import the shared contract from both your server and client. Acquire a matching **flat RPC client** -before calling `readUser`, and keep its Scope alive while the client is in use. The +Import the shared contract in both your server and client. Before calling `readUser`, acquire a matching flat RPC client. +Keep its Scope alive while you use the client. The [RPC quick start](https://ueberbrot.github.io/effect-api-query/getting-started/quick-start/) shows acquisition and cleanup. -**Shared contract — `rpc-contract.ts`** +Shared contract in `rpc-contract.ts`: ```ts import { Schema } from 'effect' @@ -38,7 +38,7 @@ export const usersRpc = RpcGroup.make( ) ``` -**Client integration — `client.ts`** +Client integration in `client.ts`: ```ts import { QueryClient } from '@tanstack/query-core' @@ -57,17 +57,17 @@ async function readUser( } ``` -Dotted tags become nested utility paths: `users.get` becomes `rpc.users.get`. RPC inputs use the -payload Schema's constructor input. +Dotted tags become nested utility paths. For example, `users.get` becomes `rpc.users.get`. +RPC inputs use the payload Schema's constructor input, including fields that its defaults can supply. ## HTTP -Import the shared HttpApi from both your server and client. Acquire its **ready HttpApiClient** -before calling `readUser`, and keep its transport resources alive while the client is in use. The +Import the shared HttpApi in both your server and client. Before calling `readUser`, acquire its ready HttpApiClient. +Keep its transport resources alive while you use the client. The [HTTP quick start](https://ueberbrot.github.io/effect-api-query/getting-started/http-quick-start/) shows acquisition and cleanup. -**Shared contract — `http-contract.ts`** +Shared contract in `http-contract.ts`: ```ts import { Schema } from 'effect' @@ -83,7 +83,7 @@ export const usersApi = HttpApi.make('users-api').add( ) ``` -**Client integration — `client.ts`** +Client integration in `client.ts`: ```ts import { QueryClient } from '@tanstack/query-core' @@ -102,22 +102,22 @@ async function readUser(client: HttpApiClient.ForApi, queryClie ``` HTTP inputs use decoded `params`, `query`, `headers`, and `payload` containers. Both examples use -the default Effect runner; provide a signal-aware `runPromiseExit` when execution needs services. -Choose a `keyPrefix` that identifies your application and any user or tenant that affects its data. +the default Effect runner. If execution needs services, provide a signal-aware `runPromiseExit`. +Choose a `keyPrefix` that identifies your application and any user or tenant that affects the returned data. Keep credentials out of keys. ## Capabilities -| Contract | Generated operations | -| --------------------------------------- | --------------------------------------------- | -| Unary RPC or buffered nonmultipart HTTP | Query, infinite query, mutation | -| Buffered nonmultipart HTTP | Separate response status/header metadata view | -| Buffered multipart HTTP | Mutation with explicit `FormData` | -| RPC stream or unambiguous HTTP SSE | Accumulated streamed query, live query | +| Contract | Generated operations | +| --------------------------------------- | -------------------------------------------- | +| Unary RPC or buffered nonmultipart HTTP | Query, infinite query, mutation | +| Buffered nonmultipart HTTP | Separate view of response status and headers | +| Buffered multipart HTTP | Mutation with explicit `FormData` | +| RPC stream or unambiguous HTTP SSE | Accumulated streamed query, live query | -Generated keys support cache reads, writes, and invalidation. Queries forward cancellation to -Effect. Use `withQueryPolicy` and `withMutationPolicy` to share typed defaults. Your application -invalidates affected queries after mutations. See +Use generated keys to read, write, and invalidate cache entries. Queries forward cancellation to +Effect. Use `withQueryPolicy` and `withMutationPolicy` to share typed defaults. +Your application decides which queries to invalidate after mutations. See [feature support](https://ueberbrot.github.io/effect-api-query/getting-started/feature-support/) for supported contracts and limits. @@ -136,12 +136,12 @@ Install the library usage skill from this repository: npx skills add ueberBrot/effect-api-query --full-depth --skill effect-api-query ``` -The same skill ships in the npm package. With the library installed, run +The npm package includes the same skill. After installing the library, run `npx @tanstack/intent@latest install` and enable `effect-api-query`, then load it with: ```sh npx @tanstack/intent@latest load effect-api-query#effect-api-query ``` -Intent uses the skill from the installed library version. GitHub skill installations update -separately through the Skills CLI. +Intent loads the skill from your installed library version. Update a skill installed from GitHub +through the Skills CLI. diff --git a/_artifacts/domain_map.yaml b/_artifacts/domain_map.yaml index fc1766a..8a742f0 100644 --- a/_artifacts/domain_map.yaml +++ b/_artifacts/domain_map.yaml @@ -7,7 +7,7 @@ library: domains: - name: Contract-derived query workflows slug: contract-queries - description: Choose typed cache views while preserving application-owned execution and lifetime. + description: Choose typed cache views. Keep execution and resource lifetime under application control. skills: - name: effect-api-query slug: effect-api-query @@ -56,13 +56,13 @@ skills: - topic: Adapter inputs and capabilities reason: RPC construction and HTTP decoded containers differ. - topic: Cache workflows - reason: Keys, policies, and native filters are library contracts; writes and replacement - are application-owned workflows. + reason: The library defines keys, policies, and native filters. Applications own writes + and owner replacement. - topic: Hydration and SSR reason: Captures, view codecs, and preparation require request lifetime. - topic: Frameworks and hosts - reason: Native providers, Suspense recovery, hook overloads, and transport boundaries are - conditional integrations rather than package-provided services. + reason: Native providers, Suspense recovery, hook overloads, and transport boundaries + depend on the integration. The package does not provide these services. failure_modes: - mistake: Reuse an accumulated key across incompatible policies mechanism: Retention and refetch policy change representation and normalized identity. @@ -72,15 +72,15 @@ skills: priority: HIGH status: active - mistake: Dispose resources after native cancellation alone - mechanism: Query settlement can precede local finalization; accepted writes and - preparation may still need the runtime. + mechanism: Query settlement can precede local finalization. Accepted writes and + preparation can still need the runtime. wrong_pattern: await queryClient.cancelQueries(); await runtime.dispose() correct_pattern: await acceptedWorkDrain; await runtime.dispose() source: examples/tanstack-start/src/lib/application.ts priority: HIGH status: active - mistake: Roll back an overlapping write with an old snapshot - mechanism: Whole-list replacement erases intervening writes; stale entity restoration + mechanism: Whole-list replacement erases intervening writes. Restoring a stale entity can overwrite a newer authoritative cache revision. wrong_pattern: queryClient.setQueryData(key, oldList) correct_pattern: Capture current entity fields and positions for each new write; restore @@ -125,7 +125,7 @@ skills: priority: HIGH status: active - mistake: Rely on Suspense cancellation for open streams - mechanism: Native Suspense hooks do not support cancellation; lifecycle cleanup needs + mechanism: Native Suspense hooks do not support cancellation. Lifecycle cleanup needs an ordinary abort-aware query or observer. wrong_pattern: useSuspenseQuery(rpc.events.watch.liveOptions()) correct_pattern: Use an ordinary Query hook or observer for a stream requiring cancellation. @@ -147,8 +147,8 @@ tensions: - name: Library contracts and application workflows skills: - effect-api-query - description: Generated options, keys, inference, and policy binding have a package contract; - native providers, transports, and domain protocols belong to the application. + description: The package defines generated options, keys, inference, and policy binding. + The application owns native providers, transports, and domain protocols. implication: Explain package behavior in library guidance, native consumers in integrations, and concrete protocols such as cancellable commands in runnable examples. - name: Exact identity and broad refresh @@ -161,15 +161,14 @@ tensions: - name: Local retirement and accepted domain work skills: - effect-api-query - description: Private cache retirement stops local side effects without pretending to undo - remote writes. + description: Private cache retirement stops local side effects. It does not undo remote writes. implication: Keep originating-owner guards after drain and model command cancellation explicitly. - name: Policy reuse and call-site inference skills: - effect-api-query description: Shared endpoint-aware defaults reduce repetition while selected data and optimistic context depend on each invocation. - implication: Bind typed policies once per owner, keeping projections, seeds, identity, and - optimistic context at the call site; compose required callbacks deliberately. + implication: Bind typed policies once per owner. Keep projections, seeds, identity, and + optimistic context at the call site. Compose required callbacks explicitly. cross_references: [] gaps: [] diff --git a/_artifacts/skill_spec.md b/_artifacts/skill_spec.md index ce41f47..ed52939 100644 --- a/_artifacts/skill_spec.md +++ b/_artifacts/skill_spec.md @@ -1,14 +1,12 @@ # Effect API Query skill coverage -The package derives TanStack Query utilities from Effect RPC and HTTP contracts -and ready clients. Applications own transport, authentication, runtime, Scope, -cache ownership, and serialization policy. +The package builds TanStack Query utilities from Effect RPC and HTTP contracts and ready clients. +Applications own transport, authentication, runtime, Scope, cache ownership, and serialization policy. ## Structure -One automatically discoverable `effect-api-query` skill covers the existing -library. Its entry point holds shared rules and routes conditional tasks to six -references: +One `effect-api-query` skill covers the library. Its activation description supports automatic discovery. +The entry point contains shared rules and directs conditional tasks to six references: | Reference | Tasks | | -------------------- | ------------------------------------------------------------------ | @@ -24,106 +22,118 @@ authoring skills. Planning records remain outside the shipped package. ## Sources and boundaries -The [domain map](domain_map.yaml) owns task coverage and failure modes. The -[skill tree](skill_tree.yaml) owns skill paths and source mappings. Public -declarations and runtime own API facts; application modules own domain and cache -coordination. Library documentation explains generated options, keys, -inference, policies, and cleanup contracts. Integration pages explain native -providers and consumers; runnable examples demonstrate application protocols. +The [domain map](domain_map.yaml) records task coverage and failure modes. +The [skill tree](skill_tree.yaml) records skill paths and source mappings. +Public declarations and runtime define API facts. Application modules define domain and cache coordination. +Library documentation explains generated options, keys, inference, policies, and cleanup contracts. +Integration pages explain native providers and consumers. Runnable examples demonstrate application protocols. Keep guidance within existing library behavior. Preserve application ownership and qualify support through [frameworks and hosts](../skills/effect-api-query/references/frameworks-and-hosts.md). -Measurements describe their fixtures; they do not establish universal budgets. +Measurements describe their fixtures. They do not establish universal budgets. ## Validation -Native Intent maintenance detects source changes and records justified outcomes -for the owned skill and planning records. Run `intent maintainer check`, report -pending work, and finish with the uncached scoped `skills-check` task. Leave -unrelated skills unresolved. +Native Intent maintenance detects source changes and records justified outcomes for the owned skill and planning records. +Run `intent maintainer check` and report pending work. Finish with the uncached scoped `skills-check` task. +Leave unrelated skills unresolved. -Strict compiler fixtures check the public declarations and retained documentation -examples. Ordinary runtime tests and application examples cover cache views, -cancellation, hydration, and ownership. Native builds validate package -declarations and exports. +Strict compiler fixtures check public declarations and documentation examples. +Runtime tests and application examples cover cache views, cancellation, hydration, and ownership. +Native builds validate package declarations and exports. The usability batch reuses the existing executable consumer fixtures: | Task | Checks | | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| Infer operation inputs, views, and failures; retain local policy inference | `tests/types/helper-contract.ts`, `tests/types/docs-policies.ts`, `tests/query-policy.test.ts` | +| Infer operation inputs, views, and failures with local policy inference | `tests/types/helper-contract.ts`, `tests/types/docs-policies.ts`, `tests/query-policy.test.ts` | | Select every input of a supported view and preserve exact tagged reads | `tests/query-view-prefix.test.ts`, `tests/types/helper-contract.ts` | -| Share typed RPC/HTTP retry decisions | `tests/types/docs-retry.ts` | +| Share typed RPC and HTTP retry decisions | `tests/types/docs-retry.ts` | | Acquire, provide, render, and dispose a minimal React application | `tests/types/docs-react-setup.tsx`, `tests/types/docs-react-entry.ts`, `examples/vite-react/tests/minimal-setup.test.tsx` | | Reset initial Suspense errors, retain failed-refetch data, start reads together | `tests/types/docs-suspense.tsx`, `examples/vite-react/tests/suspense-recovery.test.tsx` | | Trace lazy stream emissions and awaited cleanup | `tests/types/docs-stream-tracing.ts`, `examples/vite-react/tests/stream-tracing.test.ts` | -These checks verify the recommended APIs and observable behavior. A fresh isolated -consumer session remains separate evidence; this authoring session does not -establish skill discovery or consumer reliability. +These checks verify the recommended APIs and observable behavior. +A fresh isolated consumer session supplies separate evidence. +This authoring session does not establish skill discovery or consumer reliability. ## Coverage and batch history -- **0.1.0 candidate, source `6893603` (2026-10-10):** one skill and six references - cover the ten developer tasks in the domain map. Guidance includes originating - cache owners, overlapping writes, stream capture and preparation drain, paired - hydration codecs, and qualified framework and host limits. CI `38085762210` - and release validation `38085762195` passed; publication remains separate. -- **Maintainer alignment (2026-10-10), based on `6893603`:** pin native Intent - 0.5.3 under the seven-day dependency policy and add its revision-aware check to - CI. Its default discovery excludes hidden repository authoring skills. Preserve - the existing task coverage, source mappings, and package-only distribution. - Skills CLI 1.7.0 discovered and copied this same skill from the repository root: - all seven files matched, and all 13 relative links resolved within the copy. - Guidance is unchanged, so this batch checks delivery without repeating API - tasks. Scoped formatting, skill validation, and native maintainer checks with - the default and actual PR bases passed. Initial package publication remains - pending. -- **API and architecture review (2026-10-11), based on `bf38d85`:** use one - conditional-input form across buffered, metadata, streamed, and live options. - Retain the ten tasks and existing source mappings. Compiler fixtures verify - contextual inference and mutation tracking; 124 in-memory runtime cases cover - cache views, retry attempts, defaults, hydration, cancellation, and ownership. -- **Runtime and guidance audit (2026-10-11), based on `54b6521`:** qualify stream - hashing and execution errors, preserve refreshed page totals during overlapping - writes, and reconcile command starts after transport failures. Shorten repeated - guidance while retaining the ten tasks, domain map, and source mappings. All - 474 runtime cases and both public API compiler suites pass, alongside typed - lint, Effect diagnostics, dead-code checks, and documentation checks. -- **Functionality and lifetime review (2026-10-11), based on `512cd90`:** organize +- 0.1.0 candidate, source `6893603` (2026-10-10): One skill and six references + cover the ten developer tasks in the domain map. Guidance covers originating + cache owners, overlapping writes, stream capture, preparation drain, paired + hydration codecs, and framework and host limits. CI `38085762210` and release + validation `38085762195` passed. Publication remains separate. +- Maintainer alignment (2026-10-10), based on `6893603`: Pin native Intent 0.5.3 + under the seven-day dependency policy. Add its revision-aware check to CI. + Default discovery excludes hidden repository authoring skills. Preserve task + coverage, source mappings, and package-only distribution. Skills CLI 1.7.0 + discovered and copied the skill from the repository root. All seven files + matched, and all 13 relative links resolved within the copy. Guidance did not + change, so the batch checked delivery without repeating API tasks. Scoped + formatting, skill validation, and native maintainer checks passed with the + default and actual PR bases. Initial package publication remains pending. +- API and architecture review (2026-10-11), based on `bf38d85`: Use one + conditional-input form for buffered, metadata, streamed, and live options. + Retain the ten tasks and source mappings. Compiler fixtures verify contextual + inference and mutation tracking. The 124 in-memory runtime cases cover cache + views, retry attempts, defaults, hydration, cancellation, and ownership. +- Runtime and guidance audit (2026-10-11), based on `54b6521`: State the limits + of stream hashing and execution errors. Preserve refreshed page totals during + overlapping writes and reconcile command starts after transport failures. + Shorten repeated guidance while retaining the ten tasks, domain map, and + source mappings. All 474 runtime cases and both public API compiler suites + pass. Typed lint, Effect diagnostics, dead-code checks, and documentation + checks also pass. +- Functionality and lifetime review (2026-10-11), based on `512cd90`: Organize documentation around queries, mutations, pagination, streaming, and metadata. - Request disposal drains ordinary query finalizers and accepted writes; rollback - respects newer authoritative cache revisions. Clarify selected return values - and opaque middleware's encoder requirement. Preserve the ten tasks and six - references; existing source mappings cover the new guides and compiler fixture. - Remove runtime cases that duplicate static contracts, upstream behavior, or - bookkeeping. All 491 remaining runtime cases and both public API compiler - suites pass. Documentation builds with valid links, and all 38 application and - documentation Chromium checks pass. -- **Usability and policy reuse (2026-10-11), based on `8f4723c`:** extend the - existing skill and six references with operation inference, supported view - prefixes, typed query/mutation policy reuse, minimal native provider setup, - Suspense recovery, and consumed-stream tracing. Keep inputs, projections, seeds, - page mapping, stream identity, and optimistic context local; call-site callbacks - replace shared policy callbacks. Preserve native QueryClient defaults, canonical - encoded partial filtering, application lifetime, and all prior task coverage. - Semantic near-miss duplication detection found the repeated retry recipe; - shared owner invalidation and error descriptions also reduce example drift. - Expand source mappings for TSX fixtures and shared example error rendering. - A fresh consumer invoked the copied packaged skill and produced typed policy - builders in a disposable project. The behavior checks passed and rejected the - initial version without shared defaults; its result compiled on TypeScript 7 - and 5.9. Skill discovery remains unverified. Final compiler, runtime, - documentation, and revision-bound Intent outcomes are recorded with the task's - maintenance review. -- **Library-focused documentation (2026-10-11), based on `8f4723c`:** separate - library contracts, native integrations, runnable application examples, and API - reference. Keep the existing fourteen tasks and six shipped references. - Type Inference owns the detailed inference contract; Defaults and Policies owns - shared defaults and callbacks; Cache Keys and Filters replaces Cache Management. - Fold ready-client mounting into React Query and move the explicit command - protocol to Cancellable Commands. Update shipped pointers to those canonical - pages and the integration overview. Existing skill-tree source patterns already - cover the new and moved pages, so its identities and mappings remain accurate. - Preserve the fresh-consumer evidence above; this reorganization changes where - guidance is reached, without changing its recommended API behavior. + Request disposal drains ordinary query finalizers and accepted writes. + Rollback respects newer authoritative cache revisions. Clarify selected + return values and the encoder requirement for opaque middleware. Preserve + the ten tasks and six references. Existing source mappings cover the new + guides and compiler fixture. Remove runtime cases that duplicate static + contracts, upstream behavior, or bookkeeping. All 491 remaining runtime + cases and both public API compiler suites pass. Documentation builds with + valid links. All 38 application and documentation Chromium checks pass. +- Usability and policy reuse (2026-10-11), based on `8f4723c`: Extend the skill + and six references with operation inference, supported view prefixes, and + typed query and mutation policies. Add native provider setup, Suspense + recovery, and tracing of consumed streams. Keep inputs, projections, seeds, + page mapping, stream identity, and optimistic context local. Call-site + callbacks replace shared policy callbacks. Preserve native QueryClient + defaults, canonical encoded partial filtering, application lifetime, and + prior task coverage. Semantic near-miss duplication detection found the + repeated retry recipe. Shared owner invalidation and error descriptions + reduce example drift. Expand source mappings for TSX fixtures and shared + example error rendering. A fresh consumer invoked the copied packaged skill + and produced typed policy builders in a disposable project. Behavior checks + passed and rejected the initial version without shared defaults. The result + compiled on TypeScript 7 and 5.9. Skill discovery remains unverified. Final + compiler, runtime, documentation, and revision-bound Intent outcomes are + recorded in the task's maintenance review. +- Library-focused documentation (2026-10-11), based on `8f4723c`: Separate + library contracts, native integrations, runnable application examples, and + API reference. Keep the fourteen tasks and six shipped references. Type + Inference owns the detailed inference contract. Defaults and Policies owns + shared defaults and callbacks. Cache Keys and Filters replaces Cache + Management. Fold ready-client mounting into React Query and move the explicit + command protocol to Cancellable Commands. Update shipped pointers to those + canonical pages and the integration overview. Existing skill-tree source + patterns cover the new and moved pages. Skill identities and mappings remain + accurate. Preserve the fresh-consumer evidence above. This reorganization + changes where guidance is reached while retaining its recommended API behavior. +- Plain-English revision (2026-10-11), based on `0feea99`: Rewrite the docs site, + README, glossary, release guidance, ADRs, and shipped guidance. Read Humanizer, + Simple English, and the pinned technical-writing guide without installing them. + Use `writing-for-agents` for ADR decisions and shipped skill instructions. + Preserve decision context, constraints, completion checks, and conditional pointers. + Keep `.agents/`, `docs/agents/`, and `AGENTS.md` unchanged. + Remove prose dashes, shorten dense sentences, and state ownership explicitly. + Preserve examples, identifiers, links, activation metadata, fourteen tasks, + and six references. The domain map retains the same coverage and failure modes. + The skill tree retains the same identities, dependencies, and source mappings. + The documentation build passes with valid internal links. Retained examples + pass the TypeScript 7 and 5.9 compiler fixtures. Formatting and structural + skill validation pass. Final maintainer outcomes are recorded in the Intent review. + This prose revision does not repeat the prior fresh-consumer exercise or establish + new skill-discovery evidence. diff --git a/apps/docs/src/content/docs/concepts/client-lifecycle.md b/apps/docs/src/content/docs/concepts/client-lifecycle.md index 059c0ca..0e8c9cb 100644 --- a/apps/docs/src/content/docs/concepts/client-lifecycle.md +++ b/apps/docs/src/content/docs/concepts/client-lifecycle.md @@ -3,62 +3,63 @@ title: Client Lifecycle description: Keep RPC and HTTP clients, runtimes, and resources under caller ownership. --- -Both factories use ready clients. The application owns transport, middleware, runtime, -Scope, and QueryClient: +Both factories use ready clients. Your application owns the transport, middleware, runtime, +Scope, and QueryClient. Their lifetime follows this sequence: 1. Acquire the RPC or HTTP client and the services it requires. 2. Create a QueryClient and utility tree for that owner. 3. Keep those resources alive while queries, streams, and mutations run. -4. Stop consumers, cancel queries, await local stream cleanup and accepted mutations, - clear the cache, then release the client and runtime. +4. Stop consumers and cancel queries. Await local stream cleanup and accepted mutations. + Clear the cache, then release the client and runtime. -Server requests own independent resources. Browser applications keep them for their lifetime. +Each server request owns independent resources. A browser application keeps its resources alive +for its lifetime. ## HTTP clients and execution services -Calls that need no services default to `Effect.runPromiseExit`. If an exposed endpoint needs request -encoding, success decoding, or error decoding services, supply a `runPromiseExit` that provides -them. The runner must also provide any services the ready client still requires. It must forward its +Calls that need no services default to `Effect.runPromiseExit`. An exposed endpoint can require +services to encode requests or decode successes and errors. In that case, supply a `runPromiseExit` +that provides those services and any services the ready client still requires. Forward the runner's `options` argument so query cancellation reaches Effect. Omitted endpoints add no requirements. -A custom key encoder supplies cache identity only; execution still needs those services. +A custom key encoder supplies cache identity. Execution still needs its own services. -Configure HTTP authentication and request middleware when constructing the ready client. HTTP -builders have no `rpcOptions`. Include safe user or tenant identifiers in `keyPrefix` whenever middleware -changes the result for that identity. See the [HTTP factory](/effect-api-query/reference/http-factory/) -for decoded request input and the independent key-encoder contract. +Configure HTTP authentication and request middleware when you construct the ready client. +HTTP builders have no `rpcOptions`. If middleware changes results for a user or tenant, include +safe identifiers in `keyPrefix`. The [HTTP factory](/effect-api-query/reference/http-factory/) +describes decoded request input and the separate contract for key encoders. ## Request-local RPC configuration Use a builder's `rpcOptions` for metadata or configuration specific to one request, such as a -request-source header or streaming buffer size. The `context` value is local to Effect RPC client -processing; it is not a serialized server Context and does not replace the supplied Effect runner. +request-source header or stream buffer size. The `context` value applies to local Effect RPC client +processing. It is not a serialized server Context and does not replace the supplied Effect runner. -Manage authentication, middleware, transport setup, runtime services, and `Scope` in your -application client and runtime. +Your application client and runtime manage authentication, middleware, transport setup, runtime +services, and `Scope`. ## Stream creation and consumption -For an accumulated or live RPC or HTTP SSE query, the runner executes -`Stream.toAsyncIterableEffect` to create an iterable and capture its Effect `Context`. The runner -returns a successful `Exit` before Query Core pulls values. Iterator pulls then use that captured -Context while the query remains fetching. Keep its services and the ready client's Scope alive -until consumption finishes. +For accumulated and live queries over RPC or HTTP SSE, the runner executes +`Stream.toAsyncIterableEffect`. That Effect creates an iterable and captures its Effect `Context`. +The runner returns a successful `Exit` before Query Core pulls values. Those pulls use the captured +Context while the query remains fetching. Keep the Context's services and the ready client's Scope +alive until consumption finishes. -Transform or instrument the ready-client stream when the work must cover consumption. -Preserve its request options, declared value/error types, and HTTP response-mode contract. -Use a stream finalizer for completion or cancellation cleanup. +If work must cover consumption, transform or instrument the ready-client stream. +Preserve its request options, declared value and error types, and HTTP response-mode contract. +A stream finalizer handles cleanup after completion or cancellation. -A timer, span, finalizer, or retry schedule around the runner's creation Effect ends with iterable -creation. It does not wrap the later pulls. Put stream-lifetime instrumentation and recovery on the -stream itself. A retry in generated Query options reruns the query function and creates another -iterable. Coordinate it with stream and transport schedules as described in +A timer, span, finalizer, or retry schedule around the runner's creation Effect ends when it creates +the iterable. It does not cover later pulls. Put instrumentation and recovery for the stream's +lifetime on the stream itself. A retry in generated Query options reruns the query function and +creates another iterable. Coordinate query retries with stream and transport schedules as described in [retry queries](/effect-api-query/guides/retry-queries/#set-defaults-deliberately). -Native `cancelQueries` settlement can precede asynchronous iterator cleanup. Await the -resource's completion signal before releasing its services. Mutations settle separately because +Native `cancelQueries` can settle before asynchronous iterator cleanup finishes. Await the +resource's completion signal before you release its services. Mutations settle separately because they have no query abort signal. -For an open-stream server capture, await +To capture an open stream on the server, await [fetchStreamSnapshot](/effect-api-query/guides/stream-snapshots/) before dehydration or client -disposal. The helper waits for local iterator cleanup; remote completion still requires an +disposal. The helper waits for local iterator cleanup. Remote completion still requires an application acknowledgement. diff --git a/apps/docs/src/content/docs/concepts/data-normalization.md b/apps/docs/src/content/docs/concepts/data-normalization.md index be1e8e5..fc196b9 100644 --- a/apps/docs/src/content/docs/concepts/data-normalization.md +++ b/apps/docs/src/content/docs/concepts/data-normalization.md @@ -5,39 +5,39 @@ description: Understand RPC payload defaults, decoded HTTP inputs, and undefined ## Query-stable RPC payloads -A payload-bearing query constructs and normalizes its payload before it builds the key. The ready -RPC client constructs that value again during execution, so the payload Schema must be query-stable: -reconstruction must preserve every field that affects the RPC result. Constructor defaults then -describe both execution and cache identity. +A payload-bearing query constructs and normalizes its payload before it builds the key. +The ready RPC client constructs that value again during execution. The payload Schema must therefore +be query-stable: construction must preserve every field that affects the RPC result when repeated. +Constructor defaults affect both execution and cache identity. Inputs that normalize to the same payload share a cache entry. Custom key encoders also receive the normalized payload. ## Decoded HTTP inputs -HTTP builders accept the decoded `params`, `query`, `headers`, and `payload` values declared by -the endpoint. They do not apply RPC constructor defaults. Key preparation encodes those values -with their schemas; the ready HttpApiClient encodes them separately for execution. +HTTP builders accept the endpoint's declared, decoded `params`, `query`, `headers`, and `payload` +values. They do not apply RPC constructor defaults. Key preparation encodes those values with their +schemas. The ready HttpApiClient encodes them separately for execution. -Every declared request container remains required, even when its fields are optional. For example, -an endpoint with optional query filters still takes `input: { query: {} }` when no filter is selected. +Every declared request container remains required, even when its fields are optional. +An endpoint with optional query filters still takes `input: { query: {} }` when you select no filter. ## Keep captured inputs unchanged Query options retain the normalized RPC payload or decoded HTTP request for later execution. -Keep those values and their nested mutable values unchanged while the options can run. When input -changes, create new input and options rather than modifying the captured values. +Keep the retained input and any nested mutable values unchanged while the options can run. +When input changes, create new input and options. Do not modify the captured values. Canonical keys copy and deeply freeze the encoded identity. They do not clone or freeze arbitrary -execution inputs or decoded query data. RPC construction can retain nested caller values; HTTP -preparation retains the decoded request. In reactive frameworks, rebuild options inside the +execution inputs or decoded query data. RPC construction can retain nested values from the caller. +HTTP preparation retains the decoded request. In reactive frameworks, rebuild options inside the framework's computed value or accessor. ## No-content and undefined query results -TanStack Query rejects `undefined` as successful query data. When a buffered RPC or HTTP query succeeds -with `undefined`, the generated query resolves to `null` instead. The exported `QueryData` type -models that rule. +TanStack Query rejects `undefined` as successful query data. If a buffered RPC or HTTP query succeeds +with `undefined`, the generated query resolves to `null`. The exported `QueryData` type models +that rule. An HTTP endpoint using `HttpApiSchema.NoContent` decodes its successful 204 response to `undefined`, so its ordinary and infinite queries cache `null`. @@ -45,11 +45,11 @@ An HTTP endpoint using `HttpApiSchema.NoContent` decodes its successful 204 resp Mutations keep the original success type. A mutation that succeeds with `undefined` still resolves to `undefined`. -Live queries also convert each emitted `undefined` to `null`. A stream emitting a value followed -by `undefined` ends with `null` as its latest query data. Live keys, selectors, and `initialData` -use the same normalized type. The first emission makes an open live query successful while it -remains fetching; completion without any emission raises `EffectRpcQueryEmptyStreamError` for RPC -or `EffectHttpApiQueryEmptyStreamError` for HTTP SSE. +Live queries also convert each emitted `undefined` to `null`. If a stream emits a value followed +by `undefined`, its latest query data is `null`. Live keys, selectors, and `initialData` use the +same normalized type. The first emission makes an open live query successful while it remains +fetching. Completion without an emission raises `EffectRpcQueryEmptyStreamError` for RPC or +`EffectHttpApiQueryEmptyStreamError` for HTTP SSE. -Accumulated streamed queries retain emitted values as supplied, including `undefined` elements -inside their cached arrays. +Accumulated streamed queries retain each emitted value as supplied. Their cached arrays can contain +`undefined` elements. diff --git a/apps/docs/src/content/docs/concepts/http-utility-tree.md b/apps/docs/src/content/docs/concepts/http-utility-tree.md index 7a32b1f..9e52e14 100644 --- a/apps/docs/src/content/docs/concepts/http-utility-tree.md +++ b/apps/docs/src/content/docs/concepts/http-utility-tree.md @@ -3,10 +3,10 @@ title: HTTP Utility Tree description: Understand literal HTTP identifiers, top-level groups, and omitted endpoints. --- -`createHttpApiQueryUtils` builds and freezes an HTTP utility tree when you call it with a literal -Effect HttpApi declaration. Buffered endpoints without multipart have ordinary query, infinite-query, -and mutation builders. Buffered multipart endpoints have mutation builders only, regardless of HTTP method. -Endpoints with one SSE success expose accumulated and live streamed-query builders. +`createHttpApiQueryUtils` builds and freezes an HTTP utility tree from a literal Effect HttpApi +declaration. Buffered endpoints without multipart expose ordinary query, infinite-query, and +mutation builders. Buffered multipart endpoints expose mutation builders only, regardless of HTTP +method. Endpoints with one SSE success expose builders for accumulated streamed queries and live queries. | Declaration | Generated path | | ------------------------------------------ | ----------------------------------- | @@ -14,36 +14,37 @@ Endpoints with one SSE success expose accumulated and live streamed-query builde | Group `user.accounts`, endpoint `get.user` | `http['user.accounts']['get.user']` | | Top-level group `system`, endpoint `ping` | `http.ping` | -HTTP identifiers remain literal properties. They do not split on dots as RPC tags do. The API -identifier is part of the key namespace. Group and endpoint identifiers determine the path -through the tree. Keep declaration types literal so TypeScript preserves those paths and omissions. +HTTP identifiers remain literal properties. Unlike RPC tags, they do not split on dots. +The API identifier is part of the key namespace. Group and endpoint identifiers determine the path +through the tree. Literal declaration types let TypeScript preserve those paths and omissions. -Every retained branch and endpoint has `key()`. Top-level groups have no branch in the tree, but -custom key encoders still use their declaration group identifier. The factory rejects unsafe -names, collisions, and invalid encoder configuration before returning any utilities. +Every retained branch and endpoint has `key()`. A top-level group has no branch in the tree. +Custom key encoders still use that group's declaration identifier. The factory rejects unsafe +names, collisions, and invalid encoder configuration before it returns any utilities. -| Endpoint success/payload | Generated cache views | -| ------------------------------------------ | ------------------------------------------------------------ | -| Buffered without multipart | Ordinary, infinite and mutation builders; buffered metadata | -| Any buffered multipart payload alternative | Mutations only, including mixed plain/multipart alternatives | -| Single SSE success without multipart | Accumulated history and latest-value views | +| Endpoint success/payload | Generated cache views | +| ------------------------------------------ | ----------------------------------------------------------------- | +| Buffered without multipart | Ordinary, infinite, and mutation builders, plus buffered metadata | +| Any buffered multipart payload alternative | Mutations only, including mixed plain and multipart alternatives | +| Single SSE success without multipart | Accumulated history and latest-value views | -SSE events retain decoded response-header wrappers. Decoder policy contributes to both view -keys; accumulated keys also include retention and refetch policy. Endpoint prefixes select all -policies. See [HTTP Factory](/effect-api-query/reference/http-factory/) for exact builders. +SSE events retain decoded response-header wrappers. Decoder policy contributes to both view keys. +Accumulated keys also include retention and refetch policy. Endpoint prefixes select all policies. +The [HTTP Factory](/effect-api-query/reference/http-factory/) lists the exact builders. -Raw byte streams, mixed buffered/SSE successes, SSE endpoints with multipart payloads, and streaming -multipart requests omit the complete endpoint. Groups containing only omitted endpoints disappear. -Contradictory multipart metadata causes factory construction to fail. Use the ready client directly -for unsupported streams; applications own byte sinks, reconnection, and resume policy. +The factory omits endpoints with raw byte streams, mixed buffered and SSE successes, multipart SSE +payloads, or streaming multipart requests. Groups with only omitted endpoints disappear. +Contradictory multipart metadata causes factory construction to fail. Unsupported streams require +direct use of the ready client. Applications own byte sinks, reconnection, and resume policy. -Any buffered multipart payload alternative makes the endpoint mutation-only, including an endpoint -that also accepts ordinary payloads. Its leaf exposes `key()`, `mutationKey()`, and `mutationOptions()`. -Files and other mutation variables do not become cache identity, so these leaves require no key -encoder and reject encoder entries. See [upload a file](/effect-api-query/guides/http-queries-and-mutations/#upload-a-file). +Any buffered multipart payload alternative makes an endpoint mutation-only, even if the endpoint +also accepts ordinary payloads. Its leaf exposes `key()`, `mutationKey()`, and `mutationOptions()`. +Files and other mutation variables do not contribute to cache identity. These leaves require no key +encoder and reject encoder entries. The [file upload guide](/effect-api-query/guides/http-queries-and-mutations/#upload-a-file) +shows how to supply multipart input. Raw response objects remain omitted. Ordinary queries, mutations, and SSE views use decoded-only -responses; buffered metadata views copy status and headers alongside decoded data. Response -controls are excluded from request input. See the [HTTP factory](/effect-api-query/reference/http-factory/) -for request, response, and builder contracts, and [Semantic Keys](/effect-api-query/concepts/semantic-keys/) -for the `http` namespace. +responses. Buffered metadata views copy status and headers alongside decoded data. +Request input excludes response controls. The [HTTP factory](/effect-api-query/reference/http-factory/) +defines request, response, and builder contracts. [Semantic Keys](/effect-api-query/concepts/semantic-keys/) +explains the `http` namespace. diff --git a/apps/docs/src/content/docs/concepts/queries-and-mutations.md b/apps/docs/src/content/docs/concepts/queries-and-mutations.md index cdc0a38..6a7f5d1 100644 --- a/apps/docs/src/content/docs/concepts/queries-and-mutations.md +++ b/apps/docs/src/content/docs/concepts/queries-and-mutations.md @@ -3,49 +3,49 @@ title: Generated Utility Model description: Understand the contract, input, data, error, and cache identity carried by generated leaves. --- -A factory combines an Effect declaration with its ready client and a caller-supplied key prefix. -It eagerly builds an immutable utility tree. Branches represent declaration namespaces; -leaves represent RPCs or supported HTTP endpoints. Each leaf supplies the builders for the views -its contract supports. +A factory combines an Effect declaration, its ready client, and a key prefix supplied by the caller. +It builds the complete immutable utility tree when called. Branches represent declaration +namespaces. Leaves represent RPCs or supported HTTP endpoints. Each leaf supplies builders for the +views that its contract supports. ## What a generated leaf carries -| Part | Contract | -| ----------------- | ------------------------------------------------------------------------------------------ | -| Input | RPC payload constructor input or the complete decoded HTTP request | -| Query data | Decoded data for the selected cache view, before an observer's `select` | -| Execution failure | Declaration identity plus the complete Effect Cause from a failed Exit | -| Cache identity | Caller prefix, adapter/declaration path, view, canonical input, and applicable view policy | -| Options | Generated key and function with the applicable native TanStack options | +| Part | Contract | +| ----------------- | ---------------------------------------------------------------------------------------------- | +| Input | RPC payload constructor input or the complete decoded HTTP request | +| Query data | Decoded data for the selected cache view, before an observer's `select` | +| Execution failure | Declaration identity plus the complete Effect Cause from a failed Exit | +| Cache identity | Caller prefix, adapter and declaration path, view, canonical input, and applicable view policy | +| Options | Generated key and function with the applicable native TanStack options | Ordinary buffered reads cache decoded data. Infinite queries cache pages and page parameters. -Accumulated streamed queries cache emissions in order; live queries cache the latest value. -HTTP metadata caches decoded data beside immutable status and raw-header snapshots. -Mutation variables are execution input and do not become mutation-key identity. +Accumulated streamed queries cache emissions in order. Live queries cache the latest value. +HTTP metadata caches decoded data alongside immutable snapshots of the status and raw headers. +Mutation variables supply execution input and do not contribute to mutation-key identity. -See [Feature Support](/effect-api-query/getting-started/feature-support/) for supported contract -shapes. [RPC Utility Tree](/effect-api-query/concepts/rpc-utility-tree/) explains dotted tags; +[Feature Support](/effect-api-query/getting-started/feature-support/) lists supported contract +shapes. [RPC Utility Tree](/effect-api-query/concepts/rpc-utility-tree/) explains dotted tags. [HTTP Utility Tree](/effect-api-query/concepts/http-utility-tree/) explains literal group and endpoint identifiers and omitted endpoints. ## Keep input, cached data, and observer data distinct RPC constructors can supply defaults before key preparation. HTTP builders accept decoded request -containers without RPC construction. Equivalent canonical input shares identity, while distinct -views remain separate. See [Inputs and Query Data](/effect-api-query/concepts/data-normalization/) +containers without RPC construction. Equivalent canonical inputs share cache identity. +Distinct views have separate identities. See [Inputs and Query Data](/effect-api-query/concepts/data-normalization/) and [Semantic Keys](/effect-api-query/concepts/semantic-keys/). An observer's `select` projects data without changing the cached representation. Buffered and -live `undefined` values become `null`; mutation results and accumulated stream elements retain -`undefined`. Infer these types from the generated leaf when extracting reusable application code. +live `undefined` values become `null`. Mutation results and accumulated stream elements retain +`undefined`. When you extract reusable application code, infer these types from the generated leaf. ## Ownership at the boundary -The package owns generated keys/functions, view identity, and adaptation of failed Effect Exits. -TanStack owns caching, observers, retries, invalidation, and framework hooks. The application owns -ready clients, runtimes, transport, authentication, providers, lifecycle, and relationships between -writes and reads. +The package owns generated keys and functions, view identity, and adaptation of failed Effect Exits. +TanStack owns caching, observers, retries, invalidation, and framework hooks. Your application owns +ready clients, runtimes, transport, authentication, providers, lifecycle, and the relationships +between writes and reads. The same generated options work in native QueryClient calls, framework hooks, and router loaders. -[Defaults and Policies](/effect-api-query/guides/query-defaults/) keeps reusable policy attached to -the leaf's types. [Integrations](/effect-api-query/integrations/) shows each consumer's wiring. +[Defaults and Policies](/effect-api-query/guides/query-defaults/) explains how reusable policy retains +the leaf's types. [Integrations](/effect-api-query/integrations/) shows how each consumer uses the options. diff --git a/apps/docs/src/content/docs/concepts/rpc-utility-tree.md b/apps/docs/src/content/docs/concepts/rpc-utility-tree.md index ad66ae6..f61d693 100644 --- a/apps/docs/src/content/docs/concepts/rpc-utility-tree.md +++ b/apps/docs/src/content/docs/concepts/rpc-utility-tree.md @@ -3,8 +3,8 @@ title: RPC Utility Tree description: Understand how RPC tags become nested query utilities. --- -`createRpcQueryUtils` builds and freezes an RPC utility tree when you call it. It splits dotted -unary and streaming RPC tags into nested paths: +`createRpcQueryUtils` builds and freezes the complete RPC utility tree when called. Dots in unary +and streaming RPC tags define nested paths: | RPC tag | Generated path | | -------------------------- | ----------------------------------------- | @@ -13,8 +13,8 @@ unary and streaming RPC tags into nested paths: | `billing-history.list all` | `rpcQuery['billing-history']['list all']` | Each branch has `key()`. Unary leaves add ordinary query, infinite-query, and mutation builders. -Streaming leaves add accumulated-stream and live-query builders. The factory builds the tree once, -rejecting invalid tags, collisions, and invalid encoder configuration during construction. +Streaming leaves add builders for accumulated streamed queries and live queries. The factory builds +the tree once. It rejects invalid tags, collisions, and invalid encoder configuration during construction. -Reserved builder names cannot appear where they would collide with generated members. See -[Generated Builders](/effect-api-query/reference/generated-builders/) for the leaf interface. +Reserved builder names cannot occupy paths that collide with generated members. +[Generated Builders](/effect-api-query/reference/generated-builders/) describes the leaf interface. diff --git a/apps/docs/src/content/docs/concepts/semantic-keys.md b/apps/docs/src/content/docs/concepts/semantic-keys.md index 0f8dffe..e15a229 100644 --- a/apps/docs/src/content/docs/concepts/semantic-keys.md +++ b/apps/docs/src/content/docs/concepts/semantic-keys.md @@ -3,18 +3,18 @@ title: Semantic Keys description: Learn how normalized payloads produce stable cache identity. --- -For a payload-bearing RPC query, the factory constructs the payload before creating its key. -Constructor defaults therefore become part of cache identity: +For a payload-bearing RPC query, the factory constructs the payload before it creates the key. +Cache identity therefore includes constructor defaults: ```ts rpcQuery.users.get.queryKey({ id: 1 }) rpcQuery.users.get.queryKey({ id: 1, locale: 'en' }) ``` -If the RPC payload Schema supplies `locale: 'en'` as a constructor default, these calls produce the -same canonical key. +If the RPC payload Schema supplies `locale: 'en'` as a constructor default, both calls produce +the same canonical key. -The default process is: +Default key preparation follows these steps: 1. Construct the normalized payload from constructor input. 2. Encode it synchronously with the payload Schema. @@ -22,39 +22,42 @@ The default process is: 4. Store the canonical key payload in a flat, prefix-matchable query key. QueryClient owns hashing through its global and prefix defaults. Generated options omit hash -fields so fetching, stream publications, and native `getQueryData`/`setQueryData` calls address the -same cache entry. Builders reject per-call `queryKeyHashFn` and `queryHash` overrides. -Configure hashing before cache use and keep it stable while entries remain. A stream execution -rejects with `UnsupportedQueryHash` if changed hashing conflicts with a matching cached entry. +fields so fetches, stream publications, and native `getQueryData` and `setQueryData` calls address +the same cache entry. Builders reject per-call `queryKeyHashFn` and `queryHash` overrides. +Configure hashing before you use the cache, and keep it stable while entries remain. +If changed hashing conflicts with a matching cached entry, stream execution rejects with +`UnsupportedQueryHash`. -Key objects cannot contain `__proto__` or `constructor` properties at any depth. This applies to -prefixes, Schema output, and custom encoder output. +Key objects cannot contain `__proto__` or `constructor` properties at any depth. +Prefixes, Schema output, and custom encoder output all follow this restriction. RPC keys begin with the caller prefix followed by `rpc`. HTTP keys begin with the caller prefix, `http`, and the HttpApi identifier. A factory's `key()` includes these generated segments. -Every RPC operation adds its own segment after the RPC path: `query`, `infinite`, `streamed`, `live`, or -`mutation`. Concrete query keys append the canonical payload when the RPC has one. These segments -keep different cache shapes from colliding while root, branch, and RPC prefixes continue to match -all descendant operations. +Every RPC operation adds a segment after the RPC path: `query`, `infinite`, `streamed`, `live`, or +`mutation`. Concrete query keys append the canonical payload when the RPC has one. +The operation segments keep different cache shapes from colliding. Root, branch, and RPC prefixes +still match all descendant operations. -Accumulated-stream keys also include normalized retention and refetch policy. Unlimited/reset is -the default identity; different bounds or modes have separate cache entries. Supply the same policy -to `streamedKey` and `streamedOptions` for exact cache access. Broad prefixes continue to match all -views. Treat the concrete suffix as opaque. Applications persisting query data choose schema and -key-version busters as described in [Switch Cache Owners](/effect-api-query/guides/switch-cache-owners/). +Accumulated-stream keys also include normalized retention and refetch policy. Unlimited retention +with reset refetches is the default identity. Different bounds or modes have separate cache entries. +For exact cache access, supply the same policy to `streamedKey` and `streamedOptions`. +Broad prefixes still match all views. Treat the concrete suffix as opaque. + +Applications that persist query data choose schema and key-version busters as described in +[Switch Cache Owners](/effect-api-query/guides/switch-cache-owners/). Unsupported values, failed construction, and failed encoding raise `EffectRpcQueryKeyError` before -network execution. Payloads whose encoding needs services or that contain redacted values require a +network execution. Payloads that need encoding services or contain redacted values require a [custom key encoder](/effect-api-query/guides/custom-key-encoders/). HTTP keys use the endpoint's decoded request input without RPC construction. Default preparation -encodes each labelled request part with its schema, omits encoded `undefined` object members, -normalizes header names, and then freezes canonical JSON. Labels distinguish query and payload -even when both contribute URL parameters. The key reflects the declared request; it does not -infer every possible equivalence between HTTP requests. +encodes each labeled request part with its schema. It omits encoded `undefined` object members, +normalizes header names, and freezes canonical JSON. Labels distinguish query and payload even +when both contribute URL parameters. The key reflects the declared request. It does not infer +every possible equivalence between HTTP requests. -Client middleware does not contribute to that declared identity. Include safe user, tenant, or -other identifiers in the caller prefix when they affect results. See the +Client middleware does not contribute to the declared identity. If user, tenant, or other +identifiers affect results, include safe values in the caller prefix. The [HTTP factory reference](/effect-api-query/reference/http-builders/#cache-identity-and-failures) and -[HTTP encoder guide](/effect-api-query/guides/custom-key-encoders/#http-requests) for the complete rules. +[HTTP encoder guide](/effect-api-query/guides/custom-key-encoders/#http-requests) describe the complete rules. diff --git a/apps/docs/src/content/docs/examples.md b/apps/docs/src/content/docs/examples.md index 186f203..58eaf81 100644 --- a/apps/docs/src/content/docs/examples.md +++ b/apps/docs/src/content/docs/examples.md @@ -3,10 +3,9 @@ title: Executable Examples description: Run the repository's React Query and TanStack Start applications. --- -The repository contains two complete applications, each with separate RPC and HTTP views that -demonstrate both factories. They share contracts and handlers: Vite uses a separate host, while -Start serves its own routes. Both RPC endpoints accept request bodies up to 1 MiB and return -HTTP 413 for larger bodies. +The repository includes two complete applications. Each has separate RPC and HTTP views that use both factories. +The applications share contracts and handlers. Vite uses a separate host, while Start serves its own routes. +Both RPC endpoints accept request bodies up to 1 MiB and return HTTP 413 for larger bodies. | Example | Demonstrates | API host | Application URL | | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ----------------------- | @@ -15,26 +14,26 @@ HTTP 413 for larger bodies. ## Set up locally -Clone the repository, then run commands from its root: +Clone the repository and change to its root directory: ```sh git clone https://github.com/ueberBrot/effect-api-query.git cd effect-api-query ``` -Install the Node version in `.node-version` and the pnpm version in `package.json`, then install -the workspace dependencies: +Install the Node version in `.node-version` and the pnpm version in `package.json`. +Then install the workspace dependencies: ```sh pnpm install --frozen-lockfile ``` -The workspace includes Vite+. If `vp` is not on your shell's PATH, prefix the commands below with -`pnpm exec`, for example `pnpm exec vp run vite-react-dev`. +The workspace includes Vite+. If `vp` is not on your shell's PATH, prefix its commands with `pnpm exec`. +For example, run `pnpm exec vp run vite-react-dev`. -You can also use the included Dev Container: install Docker and the VS Code Dev Containers -extension, open the clone, and choose **Dev Containers: Reopen in Container**. The container -installs the declared tool versions and workspace dependencies, and forwards the application ports. +You can also use the included Dev Container. Install Docker and the VS Code Dev Containers extension. +Open the clone and choose **Dev Containers: Reopen in Container**. +The container installs the declared tool versions and workspace dependencies. It also forwards the application ports. StackBlitz WebContainers cannot run these examples because Vite+ requires a native binding that is unavailable there. @@ -47,9 +46,9 @@ vp run vite-react-dev This task starts the standalone server and the Vite development server. Vite proxies `/rpc` and `/api` to the standalone host. The HTTP and RPC handlers share one user directory. -To use another HTTP host, set `VITE_HTTP_BASE_URL` to its origin, such as -`http://127.0.0.1:3001`; the contract already supplies `/api`. The application provides the -demo authorization header through its HTTP client middleware. +To use another HTTP host, set `VITE_HTTP_BASE_URL` to its origin, such as `http://127.0.0.1:3001`. +The contract already supplies `/api`. +The application's HTTP client middleware provides the demo authorization header. ### Compare HTTP and RPC @@ -57,13 +56,13 @@ demo authorization header through its HTTP client middleware. | ------------------------------------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | Create/delete a user in either directory | Both adapters refresh through explicit cross-prefix invalidation | [application.ts](https://github.com/ueberBrot/effect-api-query/blob/main/examples/vite-react/src/lib/application.ts) | | Trigger a declared HTTP failure | HTTP execution metadata and preserved Cause | [http-section.tsx](https://github.com/ueberBrot/effect-api-query/blob/main/examples/vite-react/src/components/sections/http-section.tsx) | -| Start a slow query, wait for **Ready to cancel**, then cancel | Only that operation is interrupted; completed writes remain | [Cancellation](/effect-api-query/guides/cancellation/) | -| Switch cache owner or reload | Private caches retire; allowlisted directory DTOs may persist | [owner-cache.ts](https://github.com/ueberBrot/effect-api-query/blob/main/examples/vite-react/src/lib/owner-cache.ts) | +| Start a slow query, wait for **Ready to cancel**, then cancel | Only that operation is interrupted. Completed writes remain. | [Cancellation](/effect-api-query/guides/cancellation/) | +| Switch cache owner or reload | Private caches retire. Allowlisted directory DTOs can persist. | [owner-cache.ts](https://github.com/ueberBrot/effect-api-query/blob/main/examples/vite-react/src/lib/owner-cache.ts) | Copy [shared contracts](https://github.com/ueberBrot/effect-api-query/tree/main/examples/contracts/src), [ready-client ownership](https://github.com/ueberBrot/effect-api-query/blob/main/examples/vite-react/src/lib/application.ts), and [server handlers](https://github.com/ueberBrot/effect-api-query/tree/main/examples/server/src). -The application owns clients, runners, providers, cross-adapter invalidation and disposal. +The application owns clients, runners, providers, invalidation across adapters, and disposal. ## Run TanStack Start @@ -71,10 +70,10 @@ The application owns clients, runners, providers, cross-adapter invalidation and vp run tanstack-start-dev ``` -This task starts one full-stack process at `http://127.0.0.1:3000`, serving `/rpc` and `/api/$`. -Choose **HTTP users** for server-rendered data and hydrated navigation, or **HTTP SSR failure** -for browser refetch of a failure omitted from dehydration. Each request owns its cache and -clients; the browser owns separate resources. +This task starts one full-stack process at `http://127.0.0.1:3000`. It serves `/rpc` and `/api/$`. +Choose **HTTP users** to see server-rendered data and hydrated navigation. +Choose **HTTP SSR failure** to see the browser refetch a failure that dehydration omitted. +Each request owns its cache and clients. The browser owns separate resources. Copy the [router](https://github.com/ueberBrot/effect-api-query/blob/main/examples/tanstack-start/src/router.tsx), [SSR codecs](https://github.com/ueberBrot/effect-api-query/blob/main/examples/tanstack-start/src/lib/query-ssr.ts), @@ -83,21 +82,22 @@ See [TanStack Start](/effect-api-query/guides/tanstack-start/) for the library i ## Try cancellable commands -Use the **Cancellable commands** panel to run two commands with independent operation IDs and -cancel one while the other progresses. The [complete workflow](/effect-api-query/examples/cancellable-commands/) -explains its start/status/cancel protocol, same-ID retries, and application-owned workers. +Use the **Cancellable commands** panel to run two commands with independent operation IDs. +Cancel one while the other progresses. +The [complete workflow](/effect-api-query/examples/cancellable-commands/) +explains how to start, observe, and cancel commands. It also covers retries with the same ID and workers that the application owns. ## Pause a query until a user is selected In either app's **Choose before fetching** panel, **No user selected** sends no lookup. -Select a user to fetch its summary; clear and reselect within 30 seconds to reuse the cache. +Select a user to fetch its summary. Clear and reselect within 30 seconds to reuse the cache. Skipped and active options retain the same `select` and `staleTime`. ## Compare full and bounded stream history -After diagnostics finish, **Replay newest 2** retains the latest two states; **Replay full history** -retains all four. The live view shows only the latest state. Each accumulated policy has its own -key. See [Choose Stream History](/effect-api-query/guides/choose-stream-history/). +After diagnostics finish, **Replay newest 2** retains the latest two states. +**Replay full history** retains all four. The live view shows only the latest state. +Each accumulated policy has its own key. See [Choose Stream History](/effect-api-query/guides/choose-stream-history/). ## Inspect request-local metadata diff --git a/apps/docs/src/content/docs/examples/cancellable-commands.md b/apps/docs/src/content/docs/examples/cancellable-commands.md index 3958b94..a0fd52e 100644 --- a/apps/docs/src/content/docs/examples/cancellable-commands.md +++ b/apps/docs/src/content/docs/examples/cancellable-commands.md @@ -3,24 +3,24 @@ title: Cancellable Commands description: Run the example's explicit command protocol without treating mutation cancellation as rollback. --- -This application workflow demonstrates long-running server work with an operation ID, observable -progress, and an explicit cancellation request. Run the +This example runs long-running server work with an operation ID, progress updates, and an explicit cancellation request. +Run the [Vite React example](/effect-api-query/examples/#run-vite-react) and use the **Cancellable commands** panel. The Start application serves the same contract and handlers. -The library supplies ordinary generated query and mutation options. The application supplies the -command protocol, server workers, progress policy, and retention. For query signals and iterator -cleanup, see the [Cancellation contract](/effect-api-query/guides/cancellation/). +The library supplies generated query and mutation options. +The application defines the command protocol, owns the server workers, and decides how to report progress and retain records. +For query signals and iterator cleanup, see the [Cancellation contract](/effect-api-query/guides/cancellation/). ## Cancel a command while its mutation is pending Use an explicit cancel RPC for long-running commands. The Vite React example provides `commands.start`, `commands.status`, and `commands.cancel` in the shared contract. Each command -has a client-generated operation ID. The start mutation waits for a terminal result; a separate -query observes progress, and a separate mutation requests cancellation. +has an operation ID that the client generates. The start mutation waits for a terminal result. +A separate query observes progress, and a separate mutation requests cancellation. -In the React component setup, import `useState`, `useQuery`, and `useMutation` from their native -packages. With the example's ready `rpcQuery` and QueryClient: +In the React component, import `useState`, `useQuery`, and `useMutation` from their native packages. +Use the example's ready `rpcQuery` and QueryClient: ```ts const [operationId] = useState(() => crypto.randomUUID()) @@ -55,50 +55,50 @@ start.mutate(input) cancel.mutate(input) ``` -Disable start while pending, after success, or when status confirms an accepted command. -After a failed start with no accepted command, retry with the same operation ID; an uncertain -response must not create a second command. Disable cancel before start, while cancellation is -pending, or after a terminal status. The +Disable start while the start mutation is pending, after success, or when status confirms an accepted command. +If start fails and no command was accepted, retry with the same operation ID. +An uncertain response must not create a second command. +Disable cancel before start, while cancellation is pending, or after a terminal status. The [complete command panel](https://github.com/ueberBrot/effect-api-query/blob/main/examples/vite-react/src/components/sections/commands-section.tsx) includes the UI. -The cancel handler signals the worker and waits until it has stopped. The cancel mutation's +The cancel handler signals the worker and waits until it stops. The cancel mutation's `onSettled` callback then invalidates the generated status key. The active query refetches the -server's status even if the cancel request failed. Poll unresolved or failed status reads and -running commands. Stop at a terminal status, or when a settled start has no corresponding command. -The application chooses this invalidation policy. +server's status even if the cancel request failed. +Keep polling while a command runs or a status read remains unresolved or failed. +Stop at a terminal status, or when a settled start has no corresponding command. +The application chooses which status queries to invalidate. A cancelled command returns `{ state: 'cancelled', ... }` successfully. TanStack therefore reports -both mutations as `success` and runs their normal callbacks. The domain state does not add a -`cancelled` MutationCache state. Each operation ID has its own query key and worker, so cancelling -one command leaves another running. +both mutations as `success` and runs their normal callbacks. +The domain state does not add a `cancelled` MutationCache state. +Each operation ID has its own query key and worker. Cancelling one command leaves another running. -Run the Vite React example and use the **Cancellable commands** panel to try two concurrent -commands. The TanStack Start application hosts the same contract and handlers at `/rpc`; its -`application.rpcQuery.commands` exposes these same framework-neutral options for hooks, loaders, -and QueryClient. It can reuse the workflow above with its own QueryClient. +The TanStack Start application hosts the same contract and handlers at `/rpc`. +Its `application.rpcQuery.commands` exposes the same options for hooks, loaders, and QueryClient. +Start can reuse this workflow with its own QueryClient. ## Choose what cancellation means | Action | Result | | ------------------------- | ------------------------------------------------------------------------------------------- | -| Stop observing a query | Removes local observation; whether work stops depends on the query lifecycle and transport. | +| Stop observing a query | Removes local observation. Whether work stops depends on the query lifecycle and transport. | | Interrupt an Effect | Requests cooperative interruption and runs its finalizers. | | Call `commands.cancel` | Requests cooperative server cancellation by operation ID and waits for a terminal state. | | Compensate committed work | Requires a separate application operation that reverses or offsets prior effects. | -Transport interruption is cooperative and depends on transport support. An explicit cancel RPC is the -durable application contract for requesting server cancellation. The slow-query demo -adds a transport-specific `diagnostics.cancel` call when its local Effect is interrupted; the -package itself does not provide that behavior. +Transport interruption is cooperative and requires transport support. +An explicit cancel RPC gives the application a contract for requesting server cancellation. +When its local Effect is interrupted, the slow-query demo calls the transport-specific `diagnostics.cancel` operation. +The package does not provide that behavior. The command example owns workers in the server Scope. Unmounting a view or interrupting the -client's wait does not cancel these workers automatically. A cancel request stops future steps; -completed steps remain recorded. Cancellation after completion returns the completed result without -promising a rollback. Repeating a start with the same ID returns the existing result; a cancel -that arrives before start reserves that ID as cancelled. +client's wait does not cancel these workers automatically. +A cancel request stops future steps. Completed steps remain recorded. +Cancellation after completion returns the completed result without rolling it back. +Repeating a start with the same ID returns the existing result. +If cancel arrives before start, the server reserves that ID as cancelled. This example stores operation records in memory until the example is reset or the server stops. -Production applications need their own authorization, durable storage, and retention policy if -operations must survive restarts. The explicit RPC contract does not make this in-memory example -a durable job queue. +If operations must survive restarts, production applications need authorization, durable storage, and a retention policy. +The example's in-memory records do not provide a durable job queue. diff --git a/apps/docs/src/content/docs/getting-started/compatibility-and-stability.md b/apps/docs/src/content/docs/getting-started/compatibility-and-stability.md index dbeebb0..83ab1e3 100644 --- a/apps/docs/src/content/docs/getting-started/compatibility-and-stability.md +++ b/apps/docs/src/content/docs/getting-started/compatibility-and-stability.md @@ -3,21 +3,20 @@ title: Compatibility and Stability description: Check supported integrations, module format, and the release policy. --- -Both factories use the same public `effect-api-query` package root and peer dependencies. +Import both factories from the `effect-api-query` package root. They use the same peer dependencies. The package's [manifest](https://github.com/ueberBrot/effect-api-query/blob/main/package.json) -defines peer requirements; the [workspace catalog](https://github.com/ueberBrot/effect-api-query/blob/main/pnpm-workspace.yaml) -records the versions tested in this repository. See the +defines the peer requirements. The [workspace catalog](https://github.com/ueberBrot/effect-api-query/blob/main/pnpm-workspace.yaml) +lists the versions tested in this repository. See the [compatibility reference](/effect-api-query/reference/compatibility-and-limits/) for integration versions and limits. The package is ESM-only and targets ES2022. Enable strict TypeScript checking. -The tested Effect release is stable, but its RPC and HTTP API modules declare unstable APIs. -The package therefore pins one exact Effect peer version. Keep runtime, testing, and platform -packages on a compatible coordinated Effect set that matches it. Before changing that set, verify -the peer requirements and tested integrations of the selected package release. +The tested Effect release is stable. Its RPC and HTTP API modules declare unstable APIs, so the package pins one exact Effect peer version. +Use compatible Effect runtime, testing, and platform packages that match that version. +Before changing those packages, read the peer requirements and tested integrations for the selected `effect-api-query` release. -Query Core's streamed-query interface is experimental and may change between v5 releases. Keep -framework Query wrappers aligned with their application's Query Core version. +Query Core's streamed-query interface is experimental and can change between v5 releases. +Use framework Query wrappers that match your application's Query Core version. Choose the [RPC quick start](/effect-api-query/getting-started/quick-start/) or [HTTP quick start](/effect-api-query/getting-started/http-quick-start/) for your declaration. diff --git a/apps/docs/src/content/docs/getting-started/feature-support.md b/apps/docs/src/content/docs/getting-started/feature-support.md index ae4e62b..a656600 100644 --- a/apps/docs/src/content/docs/getting-started/feature-support.md +++ b/apps/docs/src/content/docs/getting-started/feature-support.md @@ -3,13 +3,11 @@ title: Feature Support description: Choose the RPC or HTTP adapter and find its supported TanStack Query features. --- -`effect-api-query` creates TanStack Query options and cache keys from Effect RPC groups and HttpApi -definitions. Use `createRpcQueryUtils` for RPC and `createHttpApiQueryUtils` for HTTP, both imported -from the package root. +`effect-api-query` builds TanStack Query options and cache keys from Effect RPC groups and HttpApi definitions. +Import `createRpcQueryUtils` for RPC or `createHttpApiQueryUtils` for HTTP from the package root. -Keep your native Effect declarations and ready clients. Effect and your application own the -server, transport, authentication, and resource lifetime; this package derives the Query-facing -keys, functions, and options. +Use your native Effect declarations and ready clients. Effect and your application own the server, transport, authentication, and resource lifetime. +This package builds the keys, functions, and options that TanStack Query consumes. ## Pick the operation @@ -20,19 +18,19 @@ keys, functions, and options. | Buffered multipart HTTP | Mutation with explicit `FormData` | [Upload](/effect-api-query/guides/http-queries-and-mutations/#upload-a-file) | | RPC stream / unambiguous HTTP SSE | Accumulated history or latest value | [Choose Stream History](/effect-api-query/guides/choose-stream-history/) | -Use queries for cached reads and mutations for commands; writes refresh reads only when the -application invalidates them. Raw byte streams, mixed buffered/SSE successes, streaming -multipart and raw-response modes are omitted, as are SSE endpoints with multipart payloads. +Use queries for cached reads and mutations for commands. Writes refresh reads only when your application invalidates the affected queries. +The package omits raw byte streams, mixed buffered and SSE successes, streaming multipart endpoints, and raw-response modes. +It also omits SSE endpoints with multipart payloads. See [Compatibility and Limits](/effect-api-query/reference/compatibility-and-limits/). ## Plan application ownership -Configure transport and authentication, acquire the ready client, and create any required runtime -before constructing utilities. Keep those resources alive while queries and mutations use them. Configure -TanStack providers, Devtools, persistence, broadcasting, and cache defaults in your application. +Before constructing utilities, configure transport and authentication, acquire the ready client, and create any required runtime. +Keep those resources alive while queries and mutations use them. +Your application configures TanStack providers, Devtools, persistence, broadcasting, and cache defaults. -Include a safe user or tenant identity in `keyPrefix` whenever client configuration changes the -returned data. Keep credentials out of keys. See [semantic keys](/effect-api-query/concepts/semantic-keys/) +If client configuration changes the returned data, include a safe user or tenant identity in `keyPrefix`. +Keep credentials out of keys. See [semantic keys](/effect-api-query/concepts/semantic-keys/) and [client lifecycle](/effect-api-query/concepts/client-lifecycle/). Start with the [RPC Quick Start](/effect-api-query/getting-started/quick-start/) or diff --git a/apps/docs/src/content/docs/getting-started/http-quick-start.md b/apps/docs/src/content/docs/getting-started/http-quick-start.md index 4033946..db56da7 100644 --- a/apps/docs/src/content/docs/getting-started/http-quick-start.md +++ b/apps/docs/src/content/docs/getting-started/http-quick-start.md @@ -3,15 +3,15 @@ title: HTTP Quick Start description: Acquire an HttpApiClient and use generated query and mutation options. --- -First [install the package](/effect-api-query/getting-started/installation/). The server must -serve the same `usersApi` contract at `http://localhost:3000`. Share the contract between your -server and browser application. +[Install the package](/effect-api-query/getting-started/installation/) before starting. +The server must serve the `usersApi` contract at `http://localhost:3000`. +The server and browser application must share the same contract. ## Define the shared contract Place the declaration in `http-contract.ts`. Both the server and client import this module. -**Shared contract — `http-contract.ts`** +Shared contract in `http-contract.ts`: ```ts import { Effect, Schema } from 'effect' @@ -37,10 +37,11 @@ export const usersApi = HttpApi.make('users-api').add( ## Acquire the client and execute requests -This client module acquires the ready client through a ManagedRuntime, reads a user, writes a -user, and releases its resources. Replace the base URL with your server. +The client module uses a ManagedRuntime to acquire the ready client. +It reads a user, creates a user, and then releases its resources. +Replace the base URL with your server's URL. -**Client integration — `client.ts`** +Client integration in `client.ts`: ```ts import { MutationObserver, QueryClient } from '@tanstack/query-core' @@ -83,23 +84,23 @@ try { } ``` -HTTP input is the **decoded request**. Supply the endpoint's declared `params`, `query`, `headers`, -and `payload` containers; the client handles wire encoding. HTTP builders do not apply Schema -constructor defaults: this request explicitly supplies `locale: 'en'`. +HTTP input is the decoded request. Supply the endpoint's declared `params`, `query`, `headers`, and `payload` containers. +The client encodes those values for transport. HTTP builders do not apply Schema constructor defaults. +This request supplies `locale: 'en'` explicitly. The runtime provides the HTTP transport and forwards TanStack's signal through `runPromiseExit`. -Keep the runtime and client resources alive while queries and mutations use them. Invalidation is -application policy; this example refreshes the `users` endpoint prefix after creation. +Keep the runtime and client resources alive while queries and mutations use them. +Your application decides which queries to invalidate. This example refreshes the `users` endpoint prefix after creation. -Buffered nonmultipart endpoints offer query and mutation builders regardless of HTTP method; -buffered multipart endpoints offer mutations only. Literal group/endpoint names become utility -paths; dots require bracket access, and `topLevel: true` places endpoints at the root. +Buffered nonmultipart endpoints provide query and mutation builders for every HTTP method. +Buffered multipart endpoints provide mutations only. Literal group and endpoint names become utility paths. +Use bracket access for names that contain dots. With `topLevel: true`, endpoints appear at the root. See [HTTP Factory](/effect-api-query/reference/http-factory/). ## Keep a browser client alive -The client module above awaits its mutation before disposal. For components, acquire once before -mounting and keep the client alive for the application lifetime. Follow +This client module awaits its mutation before disposal. If you use components, acquire the client once before mounting them. +Keep the client alive for the application lifetime. Follow [React client setup](/effect-api-query/guides/react-query/#acquire-and-mount-the-client) for a complete provider, component, and cleanup callback. diff --git a/apps/docs/src/content/docs/getting-started/installation.md b/apps/docs/src/content/docs/getting-started/installation.md index 60d4c3d..7a7e948 100644 --- a/apps/docs/src/content/docs/getting-started/installation.md +++ b/apps/docs/src/content/docs/getting-started/installation.md @@ -9,20 +9,19 @@ Install the package and TanStack Query Core: pnpm add effect-api-query @tanstack/query-core ``` -With pnpm's default peer installation enabled, this also installs the exact Effect release required -by the package. If your application already pins Effect or disables automatic peer installation, -install the Effect version from the package's `peerDependencies` and resolve any peer mismatch -before continuing. The [package manifest](https://github.com/ueberBrot/effect-api-query/blob/main/package.json) -and [workspace catalog](https://github.com/ueberBrot/effect-api-query/blob/main/pnpm-workspace.yaml) -define peer requirements and repository integration versions. +With pnpm's default peer installation enabled, this command also installs the exact Effect release that the package requires. +If your application already pins Effect or disables automatic peer installation, install the Effect version from the package's `peerDependencies`. +Resolve peer mismatches before continuing. The [package manifest](https://github.com/ueberBrot/effect-api-query/blob/main/package.json) +defines the peer requirements. The [workspace catalog](https://github.com/ueberBrot/effect-api-query/blob/main/pnpm-workspace.yaml) +lists the versions used in this repository. -React applications also need `@tanstack/react-query`, matching their Query Core version: +For React, install `@tanstack/react-query` with a version that matches your Query Core version: ```sh pnpm add @tanstack/react-query ``` -Use strict TypeScript checking, ESM, and an ES2022-capable application. See the +Enable strict TypeScript checking and use ESM with ES2022 support. See the [compatibility reference](/effect-api-query/reference/compatibility-and-limits/) for compiler and framework versions. Continue with the [HTTP API quick start](/effect-api-query/getting-started/http-quick-start/) or diff --git a/apps/docs/src/content/docs/getting-started/quick-start.md b/apps/docs/src/content/docs/getting-started/quick-start.md index e510177..0658014 100644 --- a/apps/docs/src/content/docs/getting-started/quick-start.md +++ b/apps/docs/src/content/docs/getting-started/quick-start.md @@ -3,15 +3,15 @@ title: RPC Quick Start description: Acquire an RPC client and use generated query and mutation options. --- -First [install the package](/effect-api-query/getting-started/installation/). The server must -serve the same `usersRpc` contract at `http://localhost:3000/rpc` with Effect's JSON RPC protocol. -Share the contract between your server and browser application. +[Install the package](/effect-api-query/getting-started/installation/) before starting. +The server must serve the `usersRpc` contract at `http://localhost:3000/rpc` with Effect's JSON RPC protocol. +The server and browser application must share the same contract. ## Define the shared contract Place the declaration in `rpc-contract.ts`. Both the server and client import this module. -**Shared contract — `rpc-contract.ts`** +Shared contract in `rpc-contract.ts`: ```ts import { Effect, Schema } from 'effect' @@ -35,10 +35,10 @@ export const usersRpc = RpcGroup.make( ## Acquire the client and execute requests -This client module acquires a ready flat client, reads a user, writes a user, and releases its -resources. Replace the URL with your server's RPC endpoint. +The client module acquires a ready flat client, reads a user, and creates a user. +It then releases its resources. Replace the URL with your server's RPC endpoint. -**Client integration — `client.ts`** +Client integration in `client.ts`: ```ts import { MutationObserver, QueryClient } from '@tanstack/query-core' @@ -85,28 +85,27 @@ try { } ``` -Dotted tags become nested paths: `users.get` becomes `rpc.users.get`. The factory uses -`Effect.runPromiseExit` by default; supply a signal-aware `runPromiseExit` when execution needs -services. Keep the client's Scope and runner alive while the utilities are used. +Dotted tags become nested paths. For example, `users.get` becomes `rpc.users.get`. +The factory uses `Effect.runPromiseExit` by default. If execution needs services, supply a signal-aware `runPromiseExit`. +Keep the client's Scope and runner alive while you use the utilities. -Query input is the RPC payload's **constructor input**: `{ id: 1 }` works because its constructor -supplies `locale: 'en'`. Defaults must be deterministic because key preparation and client execution -construct the payload separately. The decoded response is the query data; each mutation receives -its own variables when `mutate` runs. +Query input is the RPC payload's constructor input. The input `{ id: 1 }` works because the constructor supplies `locale: 'en'`. +Defaults must be deterministic because key preparation and client execution construct the payload separately. +The decoded response is the query data. Each mutation receives its own variables when `mutate` runs. -Invalidation is application policy. This example refreshes all `users` reads after creation. -A safe user or tenant identity belongs in `keyPrefix` when it affects returned data; credentials -do not. RPC and HTTP keys have separate namespaces. +Your application decides which queries to invalidate. This example refreshes all `users` reads after creation. +If a user or tenant identity affects the returned data, include a safe identifier in `keyPrefix`. +Keep credentials out of keys. RPC and HTTP keys have separate namespaces. ## Keep a browser client alive -The client module above finishes each operation before disposing its resources. For an application, -acquire once before mounting components and release after unmounting them. Use the +This client module finishes each operation before disposing its resources. +In an application, acquire the client once before mounting components and release it after unmounting them. Use the [React client setup](/effect-api-query/guides/react-query/#acquire-and-mount-the-client) for a provider, component, and cleanup callback. Its HTTP client setup can be replaced with the RPC acquisition above. -Before disposing clients that also serve streams or concurrent mutations, stop consumers and -drain queries, local stream cleanup, and accepted mutations. Follow +Before disposing clients that serve streams or concurrent mutations, stop their consumers. +Wait for queries, local stream cleanup, and accepted mutations to finish before releasing the clients. Follow [Client Lifecycle](/effect-api-query/concepts/client-lifecycle/). -For hooks, continue with [React Query](/effect-api-query/guides/react-query/); for exact method -and input contracts, use [RPC Builders](/effect-api-query/reference/generated-builders/). +For hooks, continue with [React Query](/effect-api-query/guides/react-query/). +For method and input contracts, use [RPC Builders](/effect-api-query/reference/generated-builders/). diff --git a/apps/docs/src/content/docs/guides/angular-query.md b/apps/docs/src/content/docs/guides/angular-query.md index fa84c22..b95319f 100644 --- a/apps/docs/src/content/docs/guides/angular-query.md +++ b/apps/docs/src/content/docs/guides/angular-query.md @@ -3,8 +3,8 @@ title: Angular Query description: Rebuild generated options from Angular signals and keep client lifetime in the application. --- -Build generated options inside Angular's reactive accessor so input changes produce a new key -and query function. This applies to both factories and every generated query view. +Build options inside Angular's reactive accessor. When input changes, the accessor creates a new +key and query function. Use this pattern with either factory and every generated query view. The examples target Angular 22.2.1 with Angular Query experimental 5.104.0 and Query Core 5.104.0. @@ -13,12 +13,12 @@ while their options can execute. ## Create queries in an injection context -Register `provideTanStackQuery(queryClient)` in your application providers. Construct queries -inside an Angular injection context and read the signal inside the accessor passed to `injectQuery`. +Register `provideTanStackQuery(queryClient)` in your application providers. Create queries inside +an Angular injection context. Read each signal inside the accessor passed to `injectQuery`. Create `rpc` from your [ready client](/effect-api-query/getting-started/quick-start/). -Here `users.read` accepts `{ id: number }` and returns a user with `name`. -In an existing component's field initializers, with `signal` from `@angular/core` and -`injectQuery` from `@tanstack/angular-query-experimental`: +In this example, `users.read` accepts `{ id: number }` and returns a user with `name`. +Import `signal` from `@angular/core` and `injectQuery` from +`@tanstack/angular-query-experimental`. Add these field initializers to an existing component: ```ts readonly id = signal(1) @@ -30,23 +30,23 @@ readonly user = injectQuery(() => ) ``` -Use `injectInfiniteQuery` with `infiniteOptions` and `injectMutation` with `mutationOptions`. -Supply each mutation's input through its execution variables. Angular query results expose -signals such as `data()`, `error()`, and `fetchStatus()`. +For pagination, use `injectInfiniteQuery` with `infiniteOptions`. For writes, use `injectMutation` +with `mutationOptions`. Supply each mutation's input through its execution variables. +Angular query results expose signals such as `data()`, `error()`, and `fetchStatus()`. Use the same accessor for `streamedOptions`, `liveOptions`, and HTTP `metadataOptions`. ## Choose native options carefully -Angular's defined-data overload accepts a query function rather than `skipToken`. A skipped query -therefore retains `undefined` in its `data()` type even when it has defined `initialData`. An -initial-data function that may return `undefined` also produces optional result data. +Angular's defined-data overload accepts a query function and excludes `skipToken`. A skipped query +therefore retains `undefined` in its `data()` type, even with defined `initialData`. +An initial-data function that can return `undefined` also produces optional result data. ## Own the native integration Follow the [shared integration lifetime](/effect-api-query/integrations/#own-the-cache-and-client-lifetime) -for providers, observer removal, stream finalizers, and accepted mutations. Match this native -wrapper to the application's Query Core and the framework's supported compiler/tooling. +for providers, observer removal, stream finalizers, and accepted mutations. Match the native +wrapper to the application's Query Core. Use a compiler and tooling that Angular supports. -Angular core, common, compiler, and platform-browser should share one release. Server rendering -and hydration remain application integrations. +Use the same Angular release for core, common, compiler, and platform-browser. The application +owns server rendering and hydration. diff --git a/apps/docs/src/content/docs/guides/cache-filters.md b/apps/docs/src/content/docs/guides/cache-filters.md index 85b6428..2a7780c 100644 --- a/apps/docs/src/content/docs/guides/cache-filters.md +++ b/apps/docs/src/content/docs/guides/cache-filters.md @@ -3,22 +3,23 @@ title: Cache Keys and Filters description: Read typed entries and target native cache operations with generated prefixes and exact keys. --- -Use a generated concrete key for typed `getQueryData` and `setQueryData`. Use root, branch, leaf, -or view prefixes for native `invalidateQueries`, `cancelQueries`, and query-state filters. -Import `viewPrefix` from `effect-api-query` to select one supported cache view. Choose the scope before -adding `type`, `stale`, `fetchStatus`, or `predicate` conditions: - -| Scope | Key | Matches | -| --------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------- | -| Root | `rpc.key()` or `http.key()` | All cached query views in that utility tree | -| Branch | `rpc.users.key()` | Every descendant leaf and query view | -| Leaf | `rpc.users.get.key()` | Ordinary and infinite queries; HTTP leaves also include metadata views | -| Operation | `viewPrefix(rpc.users.get, 'query')` | Ordinary queries for that leaf | -| Encoded partial input | Operation prefix followed by an encoded partial object | Entries containing those encoded fields | -| Exact entry | Generated concrete key with `exact: true` | One complete cache identity, including its stream policy | +For typed `getQueryData` and `setQueryData`, use a generated concrete key. For native +`invalidateQueries`, `cancelQueries`, and query-state filters, use root, branch, leaf, or view prefixes. +To select one supported cache view, import `viewPrefix` from `effect-api-query`. +Choose the scope before adding `type`, `stale`, `fetchStatus`, or `predicate` conditions: + +| Scope | Key | Matches | +| --------------------- | ------------------------------------------------------ | ------------------------------------------------------------------ | +| Root | `rpc.key()` or `http.key()` | All cached query views in that utility tree | +| Branch | `rpc.users.key()` | Every descendant leaf and query view | +| Leaf | `rpc.users.get.key()` | Ordinary and infinite queries, plus metadata views for HTTP leaves | +| Operation | `viewPrefix(rpc.users.get, 'query')` | Ordinary queries for that leaf | +| Encoded partial input | Operation prefix followed by an encoded partial object | Entries containing those encoded fields | +| Exact entry | Generated concrete key with `exact: true` | One complete cache identity, including its stream policy | A streaming leaf prefix includes accumulated and live views. Use `viewPrefix(leaf, 'streamed')` -or `viewPrefix(leaf, 'live')` to select one representation. HTTP buffered metadata uses `metadata`; pagination uses `infinite`. +or `viewPrefix(leaf, 'live')` to select one representation. HTTP buffered metadata uses `metadata`. +Pagination uses `infinite`. Mutation keys belong to TanStack's separate mutation cache, and query invalidation does not affect pending mutations. @@ -28,8 +29,8 @@ Invalidation refreshes affected reads according to TanStack's native policy. It or cancel pending mutations. The application chooses which read keys a successful write affects. RPC roots append `rpc` to the caller prefix. HTTP roots append `http` and the HttpApi identifier. -The factories keep separate caches even when the caller prefix is the same. Use the original -caller prefix to deliberately match both; use a generated root to match one utility tree. +The factories keep separate cache entries even when the caller prefix is the same. To match both, +use the original caller prefix. To match one utility tree, use its generated root. For response seeding and rollback, follow [Optimistic Updates](/effect-api-query/guides/optimistic-writes/). Map delivered domain events with [Refresh from Events](/effect-api-query/guides/refresh-from-events/). @@ -39,8 +40,8 @@ uses the same prefixes to register native cache policy. ## Filter RPC queries -These filters assume your own unary `users.get` and streaming `users.watch` declarations share -these payload fields. In their contract module, import `Effect` and `Schema` from `effect`: +In these examples, unary `users.get` and streaming `users.watch` declarations share the same +payload fields. In your contract module, import `Effect` and `Schema` from `effect`: ```ts const payload = { @@ -72,24 +73,24 @@ await queryClient.invalidateQueries({ queryKey, exact: true }) Partial matching compares stored canonical key payloads. Here `FiniteFromString` encodes the numeric constructor input `1` as `"1"`, so the partial object must use `id: "1"`. A raw `id: 1` matches no -entry. An omitted field places no constraint; specifying `locale: "en"` selects that locale. +entry. An omitted field places no constraint. Specifying `locale: "en"` selects that locale. Nested objects match recursively, arrays match by position, and scalar values must match exactly. A scalar string filter does not perform substring matching. Use the concrete builder for complete constructor input. Calling `queryKey({ id: 1 })` materializes -the default `locale: "en"`; its key therefore cannot express every locale for user 1. Passing only +the default `locale: "en"`. That key cannot express every locale for user 1. Passing only `{ locale: "en" }` rejects the missing required `id`. Do not cast partial constructor input into a key builder or run it through the full payload constructor to derive a broad filter. -`[...viewPrefix(rpc.users.watch, 'streamed'), { id: '1' }]` stops before the stream-policy suffix and matches every retention bound and -refetch mode for that encoded user. For one history, use +`[...viewPrefix(rpc.users.watch, 'streamed'), { id: '1' }]` stops before the stream-policy suffix. +It matches every retention bound and refetch mode for that encoded user. For one history, use `rpc.users.watch.streamedKey({ id: 1 }, { maxChunks: 2, refetchMode: 'append' })` with `exact: true`. -The omitted policy and explicit unlimited/reset policy share the same normalized identity. Keep the -remaining concrete suffix opaque; root, branch, leaf, and operation filters survive policy changes. +The omitted policy and explicit unlimited reset policy share the same normalized identity. Keep the +remaining concrete suffix opaque. Root, branch, leaf, and operation filters survive policy changes. ## Filter HTTP queries -HTTP filters retain labelled request containers. These filters assume your own `users.get` +HTTP filters retain labeled request containers. These filters assume your own `users.get` declares `id: Schema.FiniteFromString` in each of `params`, `query`, and `payload`, plus `'X-Locale': Schema.String` in `headers`. Its `users.watch` SSE endpoint declares `query: { id: Schema.FiniteFromString }`. @@ -122,28 +123,29 @@ The leaf prefix also includes metadata and infinite views. For every history pol `users.watch` request, use `[...viewPrefix(http.users.watch, 'streamed'), { query: { id: '1' } }]`. Default HTTP keys Schema-encode each request part, omit encoded `undefined` object members, and -lowercase header names. In this contract, `params: { id: "1" }` is a meaningful partial object; -`params: { id: 1 }` is not. Filter `headers: { 'x-locale': 'en' }` even when the decoded request uses +lowercase header names. In this contract, `params: { id: "1" }` is a meaningful partial object. +The numeric value in `params: { id: 1 }` does not match. Filter `headers: { 'x-locale': 'en' }` even when the decoded request uses `'X-Locale'`. Preserve `params`, `query`, `headers`, and `payload`: `{ query: { id: "7" } }` and -`{ payload: { id: "7" } }` select different requests. An unlabelled `{ id: "7" }` matches neither. +`{ payload: { id: "7" } }` select different requests. An unlabeled `{ id: "7" }` matches neither. HTTP request input does not use RPC payload construction. ## Bound partial filters to the encoded shape A custom encoder replaces the default encoded shape. If it returns `{ entity: { identifier: "1" }, language: "en" }`, filter by -`{ entity: { identifier: "1" } }`, using the same operation prefix. `{ id: "1" }` no longer matches. +`{ entity: { identifier: "1" } }` with the same operation prefix. `{ id: "1" }` no longer matches. If it returns the scalar `"1:en"`, native partial object matching cannot recover the original `id` or locale. HTTP custom encoders also replace request labels and header normalization. Applications that need partial matching can define an explicit JSON object projection in their encoder and build filters against that projection. Preserve every safe value that distinguishes -results. Treat projection changes as cache-identity changes, including persisted-cache version -busters. For opaque or noninvertible projections, use generated broad prefixes or a generated exact -key. A custom predicate can inspect only the information retained in the key or other -application-owned query metadata; it cannot reconstruct omitted input. - -Concrete builders retain DataTags for typed `getQueryData` and `setQueryData`. `viewPrefix` returns an immutable ordinary key for supported leaf views; it has no DataTag. -Manually composed prefixes and partial keys carry no query-data tag, and native `getQueriesData` does not infer a -homogeneous result from its filter. Keep broad reads as `unknown` until the application establishes -the matched representation; an explicit generic or cast does not validate cached values. +results. If the projection changes, update cache identity, including version busters for persisted +caches. For opaque or noninvertible projections, use generated broad prefixes or a generated exact +key. A custom predicate can inspect only information retained in the key or application-owned +query metadata. It cannot reconstruct omitted input. + +Concrete builders retain DataTags for typed `getQueryData` and `setQueryData`. `viewPrefix` returns +an immutable ordinary key for supported leaf views. That key has no DataTag. +Manually composed prefixes and partial keys carry no query-data tag. Native `getQueriesData` does +not infer a homogeneous result from its filter. Keep broad reads as `unknown` until the +application establishes the matched representation. An explicit generic or cast does not validate cached values. diff --git a/apps/docs/src/content/docs/guides/cancellation.md b/apps/docs/src/content/docs/guides/cancellation.md index e570f9c..61abd2a 100644 --- a/apps/docs/src/content/docs/guides/cancellation.md +++ b/apps/docs/src/content/docs/guides/cancellation.md @@ -3,14 +3,14 @@ title: Cancellation description: Cancel queries and request server-side cancellation for long-running commands. --- -In the application client module, forward TanStack's `AbortSignal` to your runtime. +In your application client module, forward TanStack's `AbortSignal` to the runtime. Import `RunPromiseExit` from `effect-api-query`: ```ts const runPromiseExit: RunPromiseExit = (effect, options) => runtime.runPromiseExit(effect, options) ``` -In a loader or event handler using the ready RPC utility tree, cancel one request with its generated key: +In a loader or event handler, use the ready RPC utility tree to cancel a request with its generated key: ```ts const input = { durationMs: 60_000, operationId: 'slow-query' } @@ -22,17 +22,17 @@ await queryClient.cancelQueries({ }) ``` -TanStack aborts the signal. The runner must translate that abort into Effect interruption, and the -RPC transport must support interruption if the server operation should stop. `effect-api-query` -does not add a transport-specific cancellation protocol. +TanStack aborts the signal. The runner must translate the abort into Effect interruption. +To stop the server operation, the RPC transport must also support interruption. +`effect-api-query` does not add a transport-specific cancellation protocol. Infinite page requests, accumulated streams, and live queries use the same signal. Cancelling a -stream query also calls `return()` on its AsyncIterator, which runs the Effect stream's finalizers. +stream query also calls `return()` on its AsyncIterator. That call runs the Effect stream's finalizers. TanStack also cancels an active stream when its last observer unmounts or a refetch supersedes it. -These cancellation guarantees apply to ordinary query observers. TanStack documents that +These guarantees apply to ordinary query observers. TanStack documents that cancellation does not work with `useSuspenseQuery`, `useSuspenseQueries`, and -`useSuspenseInfiniteQuery`; generated options and signal forwarding preserve that limitation. +`useSuspenseInfiniteQuery`. Generated options and signal forwarding preserve that limitation. Choose ordinary hooks when your screen depends on cancellation, particularly for open streams. See [TanStack's limitations](https://tanstack.com/query/latest/docs/framework/react/guides/query-cancellation#limitations) and [Suspense and Error Recovery](/effect-api-query/guides/suspense-and-recovery/). @@ -43,14 +43,14 @@ a mutation automatically. ## Cancel an HTTP query Use `queryClient.cancelQueries` with the HTTP endpoint's generated `queryKey(input)` or `key()` -prefix. Generated HTTP query functions pass the signal to the supplied runner; the default Effect -runner interrupts the ready HttpApiClient, and a fetch transport aborts its pending request. +prefix. Generated HTTP query functions pass the signal to the supplied runner. The default Effect +runner interrupts the ready HttpApiClient. A fetch transport aborts its pending request. Custom runners must forward the signal to preserve this behavior. TanStack retains its native query-cancellation result and cache policy. -HTTP interruption stops local client execution cooperatively. A closed connection does not prove -that server work stopped, and it does not undo a completed write. Durable server cancellation -requires an explicit application operation with an identity assigned before work begins. +HTTP interruption cooperatively stops local client execution. A closed connection does not prove +that server work stopped or undo a completed write. For durable server cancellation, provide an +application operation with an identity assigned before work begins. Reversing completed work requires a separate compensation operation. HTTP mutations receive no query abort signal and keep TanStack's normal mutation lifecycle. @@ -62,7 +62,7 @@ cancellable, assign an operation ID before starting it and provide an applicatio that requests cancellation. Observe its terminal domain state through a separate read. An interrupted client or closed HTTP connection does not establish remote completion. Cancellation -requests stop future work cooperatively; completed work needs a separate compensation operation. +requests cooperatively stop future work. Completed work needs a separate compensation operation. Use an application acknowledgement when you must wait for server cleanup. The [Cancellable Commands example](/effect-api-query/examples/cancellable-commands/) shows one @@ -71,10 +71,10 @@ concurrent commands, and the limits of its in-memory workers. ## Release resources after local cleanup -Cancelling a query and releasing its services are separate steps. Native `cancelQueries` settlement -can precede asynchronous iterator finalizers. Keep the ready client, its Scope, and the runner alive -until application-owned cleanup signals settle. Mutations settle independently because they have -no query abort signal. +Cancel a query before releasing its services. Native `cancelQueries` can settle before asynchronous +iterator finalizers finish. Keep the ready client, its Scope, and the runner alive until the +application's cleanup signals settle. Mutations settle independently because they have no query +abort signal. Follow [Client Lifecycle](/effect-api-query/concepts/client-lifecycle/) for disposal. Use [Stream Snapshots](/effect-api-query/guides/stream-snapshots/) for a finite capture that waits for diff --git a/apps/docs/src/content/docs/guides/choose-stream-history.md b/apps/docs/src/content/docs/guides/choose-stream-history.md index 48e2ee1..87dfe29 100644 --- a/apps/docs/src/content/docs/guides/choose-stream-history.md +++ b/apps/docs/src/content/docs/guides/choose-stream-history.md @@ -3,7 +3,7 @@ title: Choose Stream History description: Choose retained history, transport buffering, and update rates for long-running streams. --- -Choose the cached representation from what the screen needs: +Choose a cached representation that fits the screen: | Need | Builder | Retained data | | ---------------------------- | ---------------------------------- | --------------------------------------------- | @@ -11,19 +11,19 @@ Choose the cached representation from what the screen needs: | A recent event window | `streamedOptions` with `maxChunks` | Up to the specified number after an emission. | | Current state | `liveOptions` | The latest emitted value. | -For a long-running event display, choose a finite `maxChunks`. For current progress or a full-state -feed, choose a live query. Unlimited accumulation grows with the number of emissions; it suits -finite histories or workloads where that growth is deliberate. Persist durable history outside -the query cache when it must survive cache eviction or application restarts. +For a long-running event display, choose a finite `maxChunks`. For current progress or a feed of +complete state, choose a live query. Unlimited accumulation grows with each emission. +Use it for finite histories or workloads that deliberately retain every emission. +If history must survive cache eviction or application restarts, persist it outside the query cache. These choices apply to both RPC streams and supported HTTP SSE endpoints. Live queries normalize -successful `undefined` to `null`; accumulated histories preserve their emitted values. +successful `undefined` to `null`. Accumulated histories preserve their emitted values. ## Set retention and buffering separately -`maxChunks` limits the number of cached elements after each new emission. Seeded or hydrated history -can exceed the bound until another element arrives. One element can contain a large object or array, -so this bound does not limit bytes. It discards older elements while preserving the remaining order. +`maxChunks` limits cached elements after each new emission. Seeded or hydrated history can exceed +the bound until another element arrives. One element can contain a large object or array, so the +bound does not limit bytes. The cache discards older elements and preserves the remaining order. Use the same retention and refetch policy when building an exact key and its options. RPC `rpcOptions.streamBufferSize` controls the ready client's transport buffer before consumption. @@ -31,26 +31,26 @@ It does not set cache history or the rate of cache writes. HTTP transport buffer the ready HTTP API client. Changing a history bound does not slow the producer. An initial accumulated or live fetch exposes its first value while the stream remains open. -An accumulated `replace` refetch publishes its replacement only after successful completion; -an indefinitely open replacement therefore keeps showing the previous history. See +An accumulated `replace` refetch publishes its replacement only after successful completion. +If the replacement stream stays open indefinitely, the previous history remains visible. See [Stream Snapshots](/effect-api-query/guides/stream-snapshots/) for finite server captures. ## Assess writes and references separately -Accumulated streams build new history arrays without mutating previous publications. A history -bound limits their length; each consumed value still contributes to cache updates. Live queries -also accept each value, including repeated values. Native structural sharing can preserve equal -data references without suppressing those cache writes. +Accumulated streams build new history arrays without changing previous publications. A history +bound limits array length. Each consumed value still contributes to cache updates. +Live queries also accept each value, including repeated values. Native structural sharing can +preserve equal data references without suppressing those cache writes. For JSON-compatible values, TanStack's default structural sharing can reuse unchanged objects and -nested values. A moving window changes array positions, so sharing can also produce additional -distinct references. Compare the data shape your application receives before choosing +nested values. A moving window changes array positions, so sharing can produce more distinct +references. Compare the data shape your application receives before choosing `structuralSharing: false` or a custom native policy. -Cache notifications also include query creation and fetch lifecycle changes. Count data writes, -data reference changes, and retained elements separately. Framework observer selection and -render scheduling add their own behavior; cache notification counts do not count component renders. +Cache notifications also include query creation and changes during fetching. Count data writes, +data reference changes, and retained elements separately. Framework observer selection and render +scheduling affect rendering. Cache notification counts do not count component renders. -If the producer outruns the interface, set an explicit rate or sampling policy in the producer or -an application-owned Effect Stream transformation. Sampling intentionally discards values. -Retention and transport buffering serve different purposes from publication rate. +If the producer outruns the interface, set a rate or sampling policy in the producer or an +application-owned Effect Stream transformation. Sampling intentionally discards values. +Retention, transport buffering, and publication rate have separate purposes. diff --git a/apps/docs/src/content/docs/guides/conditional-queries.md b/apps/docs/src/content/docs/guides/conditional-queries.md index 5cc2041..5c739aa 100644 --- a/apps/docs/src/content/docs/guides/conditional-queries.md +++ b/apps/docs/src/content/docs/guides/conditional-queries.md @@ -3,13 +3,13 @@ title: Conditional Queries description: Pause queries until their required input is available. --- -In component setup, import the native `useQuery` hook and use your application's ready utility -tree, named `rpcQuery` here. Its `users.get` RPC takes a numeric `id` with a default locale and -returns a user with an `id` and `name`. Follow the [RPC quick start](/effect-api-query/getting-started/quick-start/) -for the factory setup pattern. +In component setup, import the native `useQuery` hook. Use your application's ready utility tree, +named `rpcQuery` here. Its `users.get` RPC takes a numeric `id` with a default locale and returns a +user with an `id` and `name`. For factory setup, follow the +[RPC quick start](/effect-api-query/getting-started/quick-start/). -`userId` is the selected ID, which may be undefined. Use `{ input: skipToken }` when an -input-bearing query has no valid input yet and needs TanStack options such as `staleTime`, +`userId` is the selected ID and can be undefined. If an input-bearing query has no valid input, +use `{ input: skipToken }`. The builder still accepts TanStack options such as `staleTime`, `select`, or `initialData`: ```ts @@ -27,8 +27,8 @@ const userOptions = rpcQuery.users.get.queryOptions({ const user = useQuery(userOptions) ``` -For an HTTP `users.get` endpoint with only a numeric `id` param and a user response, its complete -decoded request contains only `params`: +For an HTTP `users.get` endpoint that accepts only a numeric `id` param and returns a user, +the complete decoded request contains only `params`: ```ts const httpUserOptions = http.users.get.queryOptions({ @@ -39,24 +39,24 @@ const httpUserOptions = http.users.get.queryOptions({ ``` The exported `skipToken` is Query Core's exact sentinel. A skipped query uses its operation -prefix as its key, without payload or request identity. Skipping an HTTP query does not encode a -request or invoke the client. The builder preserves caller options and their selected-data types, -and removes `input` before returning the options to TanStack. +prefix as its key, without payload or request identity. A skipped HTTP query does not encode a +request or invoke the client. The builder preserves caller options and their selected-data types. +It removes `input` before returning the options to TanStack. Supplied `initialData` remains available. React Query's `useQuery` still types skipped hook data as possibly `undefined`, even with an initial value, because its defined-data overload excludes `skipToken`. `useInfiniteQuery` preserves its defined-data overload when `initialData` is a defined -value or factory, including for conditional and skipped inputs. +value or factory. This also applies to conditional and skipped inputs. -The sentinel applies to input-bearing `queryOptions`, `infiniteOptions`, `streamedOptions`, and -`liveOptions` in both adapters, and buffered HTTP `metadataOptions`. Inputless operations run without input, and key and -mutation builders do not accept `skipToken`. TanStack suspense and prefetch-only hooks also reject -skipped options at the type level. +Both adapters accept the sentinel in input-bearing `queryOptions`, `infiniteOptions`, +`streamedOptions`, and `liveOptions`. Buffered HTTP `metadataOptions` also accepts it. +Inputless operations run without input. Key and mutation builders do not accept `skipToken`. +TanStack suspense and prefetch-only hooks reject skipped options at the type level. -`queryOptions`, `metadataOptions`, `streamedOptions`, and `liveOptions` accept concrete input or `skipToken`, -including a conditional union. Keep the conditional input inside one builder call so the observer -has one consistent callback type. Concrete inputs and the literal `skipToken` retain their precise -key and query-function types; conditional inputs preserve both possibilities. +`queryOptions`, `metadataOptions`, `streamedOptions`, and `liveOptions` accept concrete input, +`skipToken`, or a conditional union of both. Keep conditional input inside one builder call so the +observer has one consistent callback type. Concrete input and literal `skipToken` retain precise +key and query-function types. Conditional input preserves both possibilities. If your RPC group also declares `events.watch` with a string `channel` input and string emissions, build either view in that consumer: @@ -73,19 +73,20 @@ rpcQuery.events.watch.liveOptions({ }) ``` -`refetchMode` configures accumulation. The builder removes it from the returned options even when -the query is skipped. Retention, refetch, and HTTP SSE decoder policy validation still occurs for -skipped streams. Infinite queries accept a page-to-input mapper or `skipToken`, including a -conditional union, with their required `initialPageParam` and `getNextPageParam`. Choose between -the mapper and sentinel in `input`; each mapper result must be valid input. +`refetchMode` configures accumulation. The builder removes it from the returned options, even for +skipped queries. Skipped streams still validate retention, refetch, and HTTP SSE decoder policies. +Infinite queries accept a page-to-input mapper, `skipToken`, or a conditional union of both. +They also require `initialPageParam` and `getNextPageParam`. Choose the mapper or sentinel in +`input`. Each mapper result must be valid input. -A skipped query has no executable query function, so manual `refetch()` cannot run it. Supply valid input to enable it. If a complete -request is available and you need manual refetch, use `enabled: false` instead. +A skipped query has no executable query function, so manual `refetch()` cannot run it. +Supply valid input to enable the query. If you have a complete request and need manual refetch, +use `enabled: false`. For HTTP pagination, map each page parameter to the complete request and keep stable filters in every page. See [Load pages](/effect-api-query/guides/pagination/) for initial-request identity and cursor progression. Try this in either [executable example](/effect-api-query/examples/#pause-a-query-until-a-user-is-selected). -The **Choose before fetching** control demonstrates pausing, selecting a user, and reusing fresh +The **Choose before fetching** control pauses queries, selects a user, and reuses fresh cached data after clearing and reselecting the same user. diff --git a/apps/docs/src/content/docs/guides/custom-key-encoders.md b/apps/docs/src/content/docs/guides/custom-key-encoders.md index 15c09e3..13574de 100644 --- a/apps/docs/src/content/docs/guides/custom-key-encoders.md +++ b/apps/docs/src/content/docs/guides/custom-key-encoders.md @@ -6,12 +6,13 @@ description: Supply safe semantic identity for RPC payloads and HTTP requests. ## RPC payloads By default, the factory constructs the RPC payload and synchronously encodes it with its Schema. -Supply a custom encoder when encoding requires Effect services, uses opaque encoding middleware, -or the payload contains `Schema.Redacted` values, including schemas reached through `Schema.suspend`. -The runtime check treats middleware conservatively even when it is synchronous and service-free. +If encoding requires Effect services or uses opaque encoding middleware, supply a custom encoder. +Payloads containing `Schema.Redacted` values also require one, including schemas reached through +`Schema.suspend`. The runtime check treats middleware conservatively, even when it is synchronous +and service-free. -In the application client module, configure the ready RPC client. Here `identifySecret` is -your synchronous public-identity projection: +In your application client module, configure the ready RPC client. This example uses +`identifySecret` to synchronously produce a public identity: ```ts const rpcQuery = createRpcQueryUtils(rpcGroup, { @@ -24,15 +25,16 @@ const rpcQuery = createRpcQueryUtils(rpcGroup, { }) ``` -An encoder receives the normalized payload and must return a strict `JsonValue` synchronously. It -must not reveal secrets. Return a stable public identifier, digest, or other safe semantic identity. +An encoder receives the normalized payload. It must synchronously return a strict `JsonValue` +without revealing secrets. Return a stable public identifier, digest, or other safe semantic identity. Broad payload declarations such as `Schema.Unknown`, `Schema.Any`, and void-containing unions -accept encoders too. Their default keys still require strict JSON: an accepted RPC input such as -`undefined`, a bigint, or a class instance may require an encoder to supply JSON-safe identity. +accept encoders too. Their default keys still require strict JSON. Accepted RPC inputs such as +`undefined`, a bigint, or a class instance can require an encoder to supply JSON-safe identity. -Define encoders as own enumerable properties keyed by literal payload-bearing RPC tags. TypeScript -requires entries for unsafe payloads; the factory also rejects missing or unknown entries at runtime. +Define encoders as own enumerable properties keyed by literal payload-bearing RPC tags. +TypeScript requires entries for unsafe payloads. The factory also rejects missing or unknown +entries at runtime. Inputs that can produce different RPC results must produce different keys. @@ -43,11 +45,9 @@ HTTP encoders receive the complete decoded request input: the declared `params`, applies defaults. Configure each HTTP encoder in `createHttpApiQueryUtils` under its literal declaration group and endpoint identifier, even for a top-level group or an identifier containing dots. -Suppose a `forms` group's `submit` endpoint accepts a number encoded as text or a string encoded -as JSON. Both schemas can encode to `"1"`, but the HTTP bodies differ. Preserve that distinction -in the key: - -In the HTTP client module, configure that endpoint's request projection: +Suppose the `forms` group's `submit` endpoint accepts a number encoded as text or a string encoded +as JSON. Both schemas can encode to `"1"`, but the HTTP bodies differ. In your HTTP client module, +configure the endpoint's request projection to preserve that distinction: ```ts const http = createHttpApiQueryUtils(contract, { @@ -64,27 +64,27 @@ const http = createHttpApiQueryUtils(contract, { }) ``` -Multiple non-multipart buffered alternatives require an encoder even when they share a content type. For buffered -binary input, return a JSON-safe representation such as `{ bytes: Array.from(payload) }`; the ready -client still receives the original `Uint8Array`. +Multiple non-multipart buffered alternatives require an encoder, even when they share a content +type. For buffered binary input, return a JSON-safe representation such as +`{ bytes: Array.from(payload) }`. The ready client still receives the original `Uint8Array`. Encoding services or explicit `Redacted` values in any request part also require an encoder. The factory treats opaque encoding middleware conservatively at runtime. An encoder supplies -identity only; the ready client still needs an execution runner when it requires services. The -factory synchronously rejects encoder entries for unknown, omitted, inputless, or mutation-only endpoints during -construction. +identity only. If the ready client requires services, it still needs an execution runner. +During construction, the factory synchronously rejects encoder entries for unknown, omitted, +inputless, or mutation-only endpoints. Buffered multipart endpoints expose mutation builders only and require no query identity encoder, including endpoints with mixed plain and multipart payload alternatives. Keep these endpoints out of -`keyEncoders`; pass `FormData` directly to their mutation instead. +`keyEncoders`. Pass `FormData` directly to their mutation. Keep ordinary authentication in client middleware. Partition the cache with safe tenant and user identifiers in `keyPrefix`, for example `['tenant', 'north', 'user', 'ada']`. The factory cannot infer identity from a client, base URL, or middleware. When excluding a secret, retain every safe value that distinguishes results. -Return strict `JsonValue` synchronously. The factory copies and freezes the result; it rejects +Return strict `JsonValue` synchronously. The factory copies and freezes the result. It rejects undefined object members, undefined or sparse array entries, non-finite numbers, cycles, and non-JSON objects. Custom output bypasses the default HTTP rules for omitting `undefined` members and normalizing headers. See the [HTTP reference](/effect-api-query/reference/http-builders/#cache-identity-and-failures) for -failure codes. Underlying error Causes remain intact and may contain values supplied by Effect. +failure codes. Underlying error Causes remain intact and can contain values supplied by Effect. diff --git a/apps/docs/src/content/docs/guides/handle-failures.md b/apps/docs/src/content/docs/guides/handle-failures.md index c90a0cc..e15c1ef 100644 --- a/apps/docs/src/content/docs/guides/handle-failures.md +++ b/apps/docs/src/content/docs/guides/handle-failures.md @@ -4,7 +4,7 @@ description: Inspect RPC and HTTP Causes and distinguish configuration and key e --- A failed RPC `Exit` becomes `EffectRpcQueryError`. The error records the RPC tag, the generated -operation that ran, and the complete Effect `Cause`. In the application's error handler: +operation, and the complete Effect `Cause`. Inspect the error in your application handler: ```ts import { Cause } from 'effect' @@ -20,10 +20,10 @@ const logRpcError = (error: unknown) => { Review the Cause for private messages and transport values before logging it. -These error classes distinguish failure stages: +Use the error class to identify the failure stage: -- `EffectRpcQueryConfigError` reports invalid configuration: synchronously during preparation, - or during stream execution with incompatible QueryClient hashing. +- `EffectRpcQueryConfigError` reports invalid configuration. It occurs synchronously during + preparation, or during stream execution when QueryClient hashing is incompatible. - `EffectRpcQueryKeyError` reports synchronous payload construction, encoding, or JSON canonicalization failures. - `EffectRpcQueryError` reports a failed RPC execution and preserves its Effect `Cause`. @@ -31,7 +31,7 @@ These error classes distinguish failure stages: If a custom runner rejects instead of returning an `Exit`, its rejection passes through unchanged. See the [error reference](/effect-api-query/reference/errors/) for stable codes and metadata. -Use [retry queries](/effect-api-query/guides/retry-queries/) for typed `Cause`/`Result` decisions, +Use [retry queries](/effect-api-query/guides/retry-queries/) for typed `Cause` and `Result` decisions, bounded attempt counts, and synchronous preparation errors. ## Inspect HTTP failures @@ -40,18 +40,19 @@ Import `isEffectHttpApiQueryError` from `effect-api-query` to recognize a failed The wrapper records `apiId`, `groupId`, `endpoint`, `method`, and `operation`. Its `cause` retains the complete original Effect Cause. Use `Cause.findError` to inspect declared endpoint errors, middleware errors, Schema errors, and HTTP client errors. Use `Cause.hasDies` and -`Cause.hasInterrupts` to distinguish defects and interruption. A Cause may contain several reasons. +`Cause.hasInterrupts` to distinguish defects and interruption. A Cause can contain several reasons. The package adds only declaration identity to execution-error metadata. The preserved Cause can contain upstream request headers, bodies, concrete URLs, responses, or Schema issue values. Review those values before logging or exposing them. The package does not sanitize the Cause. -`EffectHttpApiQueryConfigError` reports invalid configuration with the same preparation and -stream-execution timing as the RPC error. -`EffectHttpApiQueryKeyError` reports synchronous query-key preparation failures, before HTTP -execution. Mutations encode their request inside the ready client's Effect, so encoding failures -become `EffectHttpApiQueryError` instead. Runner rejections and user or TanStack callback failures -pass through unchanged when they produce no failed Exit. +`EffectHttpApiQueryConfigError` reports invalid configuration at the same stages as the RPC error: +preparation or stream execution. `EffectHttpApiQueryKeyError` reports synchronous failures during +query-key preparation, before HTTP execution. + +Mutations encode their request inside the ready client's Effect. Their encoding failures therefore +become `EffectHttpApiQueryError`. Runner rejections and user or TanStack callback failures pass +through unchanged when they produce no failed Exit. See the [HTTP factory reference](/effect-api-query/reference/http-builders/#cache-identity-and-failures) for key-error codes. diff --git a/apps/docs/src/content/docs/guides/http-metadata.md b/apps/docs/src/content/docs/guides/http-metadata.md index 700423d..f07f96a 100644 --- a/apps/docs/src/content/docs/guides/http-metadata.md +++ b/apps/docs/src/content/docs/guides/http-metadata.md @@ -3,9 +3,9 @@ title: HTTP Metadata description: Read decoded HTTP data, response status, and headers in a separate cache view. --- -Buffered HTTP endpoints with query support expose `metadataOptions`. Use your ready `http` utility -tree and QueryClient from the [HTTP quick start](/effect-api-query/getting-started/http-quick-start/). -Here, `users.get` declares `params.id` and `query.locale` and returns a user with `id` and `name`. +Buffered HTTP endpoints with query support expose `metadataOptions`. Start with the ready `http` +utility tree and QueryClient from the [HTTP quick start](/effect-api-query/getting-started/http-quick-start/). +In this example, `users.get` declares `params.id` and `query.locale`. It returns a user with `id` and `name`. ## Read status and headers @@ -18,9 +18,9 @@ const etag = current.headers['etag'] ``` The cache holds decoded `data`, numeric `status`, and raw string `headers` in a separate view. -Fetched envelopes and copied headers are immutable. Decoded data keeps its ownership, including -declared response-header wrappers. Choose safe headers before persistence or hydration. -Use `metadataOptions.queryKey` or `metadataKey(input)` for exact access; the endpoint's `key()` +Fetched envelopes and copied headers are immutable. Decoded data retains its existing ownership, +including declared response-header wrappers. Before persistence or hydration, choose safe headers. +For exact access, use `metadataOptions.queryKey` or `metadataKey(input)`. The endpoint's `key()` matches ordinary and metadata views. ## Update with an ETag @@ -40,7 +40,7 @@ const updateUser = new MutationObserver( ) ``` -Send the raw ETag **including quotes**, then read the refreshed metadata before another write: +Send the raw ETag with its quotes. Before another write, read the refreshed metadata: ```ts await updateUser.mutate({ @@ -55,5 +55,5 @@ The server enforces the precondition. Inspect its declared failure through the [HTTP execution error](/effect-api-query/guides/handle-failures/#inspect-http-failures). The invalidation prefix refreshes data and metadata entries. -Native selection, initial data, skipping, and cancellation also apply. For precise immutability +Native selection, initial data, skipping, and cancellation also apply. For details of immutability and structural-sharing behavior, see [Buffered Metadata](/effect-api-query/reference/http-builders/#buffered-metadata). diff --git a/apps/docs/src/content/docs/guides/http-queries-and-mutations.md b/apps/docs/src/content/docs/guides/http-queries-and-mutations.md index 3c878b3..59fe7d4 100644 --- a/apps/docs/src/content/docs/guides/http-queries-and-mutations.md +++ b/apps/docs/src/content/docs/guides/http-queries-and-mutations.md @@ -4,7 +4,7 @@ description: Supply decoded request parts and execute buffered multipart mutatio --- Use the HTTP utility tree from the [HTTP quick start](/effect-api-query/getting-started/http-quick-start/). -Keep base URL, middleware, authentication, and execution services on your ready client and runner. +Configure the base URL, middleware, authentication, and execution services on your ready client and runner. For shared builders, see [Queries](/effect-api-query/guides/queries/), [Mutations](/effect-api-query/guides/mutations/), [Pagination](/effect-api-query/guides/pagination/), [Streaming](/effect-api-query/guides/streaming/), and [HTTP Metadata](/effect-api-query/guides/http-metadata/). @@ -12,8 +12,8 @@ For shared builders, see [Queries](/effect-api-query/guides/queries/), ## Supply decoded request parts HTTP input contains the declared `params`, `query`, `headers`, and `payload` containers. -Include each declared container even when its fields are optional. For `users.get` declaring -`params.id` and `query.locale`: +Include each declared container, even when its fields are optional. This `users.get` endpoint +declares `params.id` and `query.locale`: ```ts const httpUserOptions = http.users.get.queryOptions({ @@ -23,9 +23,9 @@ const httpUserOptions = http.users.get.queryOptions({ const httpUser = await queryClient.query(httpUserOptions) ``` -The ready client encodes decoded values; HTTP builders apply no RPC constructor defaults. +The ready client encodes decoded values. HTTP builders apply no RPC constructor defaults. Inputless endpoints use `queryOptions()`. Raw response controls are excluded from request input. -Captured values remain caller-owned, so keep them unchanged while using the options. +You own the captured values. Keep them unchanged while the options can execute. ## Upload a file @@ -60,9 +60,9 @@ payload.set('file', new Blob(['profile notes'], { type: 'text/plain' }), 'notes. const uploaded = await uploadFile.mutate({ params: { id: 1 }, payload }) ``` -The ready client owns encoding; the adapter returns decoded success. Any buffered multipart -payload alternative makes the endpoint mutation-only. Mutation keys exclude variables and files, -so omit these endpoints from `keyEncoders`; configured encoder entries are rejected. +The ready client owns encoding. The adapter returns decoded success. Any buffered multipart +payload alternative makes the endpoint mutation-only. Mutation keys exclude variables and files. +Omit these endpoints from `keyEncoders`, because the factory rejects their encoder entries. Streaming successes or streaming multipart alternatives omit the endpoint entirely. See the [HTTP Factory](/effect-api-query/reference/http-factory/) for exact capability boundaries diff --git a/apps/docs/src/content/docs/guides/hydrate-query-views.md b/apps/docs/src/content/docs/guides/hydrate-query-views.md index 49afbfe..e5ad3ce 100644 --- a/apps/docs/src/content/docs/guides/hydrate-query-views.md +++ b/apps/docs/src/content/docs/guides/hydrate-query-views.md @@ -3,9 +3,9 @@ title: Hydrate Query Views description: Preserve infinite pages, stream histories, live values, and HTTP metadata across JSON hydration and refetch. --- -Choose one codec for each cached representation. A success Schema describes a decoded result; -infinite pages also contain page parameters, and HTTP metadata adds an envelope. Keep decoded -values in the Query Client and encode only the snapshot sent to the browser. +Choose one codec for each cached representation. A success Schema describes a decoded result. +Infinite pages also contain page parameters, and HTTP metadata adds an envelope. Keep decoded +values in the Query Client. Encode only the snapshot sent to the browser. This task extends the [paired Profile codec](/effect-api-query/guides/hydrate-unary-data/). The server and browser own independent ready clients, runtimes, Scopes, and Query Clients. @@ -15,7 +15,7 @@ Use the same contract, input, key prefix, and Query Client hashing defaults on b Reserve a key prefix of `['hydration-views', codecId, ...applicationIdentity]` when creating each utility tree. The generated key suffix retains its usual operation, input, and view policies. -Choose `codecId` from the map below; add your owner identity after it when data depends on an owner. +Choose `codecId` from the map below. If data depends on an owner, add the owner identity after it. | Codec ID | Cached representation | | ----------------- | ------------------------------------------------------------- | @@ -28,7 +28,7 @@ Choose `codecId` from the map below; add your owner identity after it when data | `wrapped-live` | Latest decoded HTTP header wrapper | Define the view codecs in the application's hydration module. Import `Schema` and -`SchemaTransformation` from `effect`; reuse the paired `Profile` codec. The +`SchemaTransformation` from `effect`. Reuse the paired `Profile` codec. The [complete typed recipe](https://github.com/ueberBrot/effect-api-query/blob/main/tests/types/docs-hydration-views.ts) includes wrapper codecs, codec dispatch, and awaited snapshot preparation. @@ -74,56 +74,56 @@ const ProfileMetadata = Schema.Struct({ ``` Use a `Cursor` as `infiniteOptions.initialPageParam`. Return the next Cursor from -`getNextPageParam`, and let the deterministic `input` mapper convert its offset into your -contract's request input. `ProfilePages` encodes both arrays: its page parameters become decimal -strings at the JSON boundary and regain their Cursor prototype and methods during hydration. +`getNextPageParam`. Let the deterministic `input` mapper convert its offset into your contract's +request input. `ProfilePages` encodes both arrays. Page parameters become decimal strings at the +JSON boundary and regain their Cursor prototype and methods during hydration. A result Schema alone cannot choose this page-parameter representation. -Accumulated history preserves decoded `undefined` elements. Ordinary JSON would turn an +Accumulated history preserves decoded `undefined` elements. Ordinary JSON turns an undefined array element into `null`, so `UndefinedElement` encodes it as a distinct tagged value. A real `null` remains `null`. Live queries already normalize an entirely undefined emission to -`null`; their codec retains that value. +`null`. Their codec retains that value. For an HTTP header wrapper, normalization applies to the whole emitted value. A wrapper whose `body` is undefined remains a wrapper with an undefined body. `WrappedElement` preserves that body with the same tagged encoding and reconstructs the wrapper with -`HttpApiSchema.withHeaders`. `WithHeaders` is an HTTP declaration; use explicit body/header -codecs at the JSON boundary so rich body values and decoded numeric headers regain their types. +`HttpApiSchema.withHeaders`. `WithHeaders` is an HTTP declaration. At the JSON boundary, use +explicit codecs for the body and headers so rich body values and decoded numeric headers regain their types. `BufferedProfile` includes both declared HTTP successes. Adapt that union to your endpoint and its status declarations. `ProfileMetadata` preserves decoded data, numeric status, and raw -string headers. Hydrated envelopes belong to the application and remain unfrozen. A subsequent -metadata fetch produces the library's frozen envelope and raw headers again; decoded data stays -mutable. Select public response headers before sending metadata that contains private values. +string headers. Hydrated envelopes belong to the application and remain unfrozen. A later metadata +fetch produces the library's frozen envelope and raw headers again. Decoded data stays mutable. +Before sending metadata that contains private values, select public response headers. ## Prepare the complete snapshot before continuing After prefetching finite queries, run `prepareViewSnapshot(serverQueryClient)` through the -application's runner with `SnapshotEncoding` provided. Supply any asynchronous encoding work -through `beforeEncode`, then await the returned JSON before publishing it through your framework. +application's runner with `SnapshotEncoding` provided. Supply asynchronous encoding work through +`beforeEncode`. Await the returned JSON before publishing it through your framework. The original cache keeps decoded classes, bigint values, byte arrays, and page parameters. For an open HTTP or RPC stream, first await [fetchStreamSnapshot](/effect-api-query/guides/stream-snapshots/) with its exact generated stream options. It captures the first visible value and awaits local iterator cleanup. Keep exclusive -ownership of that idle, unobserved key during capture. Only then prepare JSON and dispose the -server's ready-client resources. Local cleanup does not acknowledge remote cancellation. +ownership of that idle, unobserved key during capture. After capture settles, prepare JSON and +dispose the server's ready-client resources. Local cleanup does not acknowledge remote cancellation. On the browser, provide `SnapshotDecoding` and await `prepareViewHydration(browserQueryClient, -json)` before querying or mounting observers. Encoding and decoding require separate services; -one cannot satisfy the other. All selected data decodes before a single native `hydrate` call, -so invalid codec data or an unknown codec ID leaves the browser cache untouched. +json)` before querying or mounting observers. Encoding and decoding require separate services. +One cannot satisfy the other. Preparation decodes all selected data before a single native +`hydrate` call. Invalid codec data or an unknown codec ID leaves the browser cache untouched. The preparation helpers accept the matching trusted server's native dehydrated envelope and validate selected query data. Validate arbitrary external snapshots separately. Handle malformed JSON and preparation failures in the application's rendering or hydration error path. Only successful queries in the reserved family enter the snapshot. Mutations and failed-query -errors/Causes are omitted. Hydration therefore leaves an omitted failed query available for a +errors and Causes are omitted. Hydration leaves an omitted failed query available for a new browser fetch. Error hydration needs its own application-safe representation. Choose native freshness defaults to control duplicate reads. A successful fresh hydrated query -can reuse its reconstructed data; later invalidation and refetch use the browser's independent +can reuse its reconstructed data. Later invalidation and refetch use the browser's independent ready client and the same decoded representation. For streams, explicit cached capture acquires -no new work; fresh capture starts the browser stream again. Keep Effects and Promises outside -query data and finish preparation before publishing or hydrating. +no new work. Fresh capture starts the browser stream again. Keep Effects and Promises outside +query data. Finish preparation before publishing or hydrating. diff --git a/apps/docs/src/content/docs/guides/hydrate-unary-data.md b/apps/docs/src/content/docs/guides/hydrate-unary-data.md index 89906ee..1ef9b55 100644 --- a/apps/docs/src/content/docs/guides/hydrate-unary-data.md +++ b/apps/docs/src/content/docs/guides/hydrate-unary-data.md @@ -3,19 +3,19 @@ title: Hydrate Unary Data description: Keep DTOs or rich Schema values consistent across JSON hydration and client refetch. --- -Choose the cache representation before connecting server rendering to TanStack Query. The server -and browser must use the same contract, key prefix, and inputs. Each owns its Query Client, ready -Effect client, and runtime. Cancel outstanding queries before disposing a runtime. +Before connecting server rendering to TanStack Query, choose the cache representation. The server +and browser must use the same contract, key prefix, and inputs. Each owns a Query Client, ready +Effect client, and runtime. Before disposing a runtime, cancel outstanding queries. -These recipes cover unary profile reads through either factory. They keep only successful profile +The recipes cover unary profile reads through either factory. They keep only successful profile queries and omit mutations. Use the [query-view recipe](/effect-api-query/guides/hydrate-query-views/) for infinite pages, stream snapshots, and HTTP metadata. ## Keep DTOs in both caches Use a JSON-safe success schema when components need plain data. The contract below keeps credits -as decimal strings and binary data as base64 strings. Return this DTO from the server operation; -the ready RPC or HTTP client produces the same DTO on the server and browser. +as decimal strings and binary data as base64 strings. Return this DTO from the server operation. +The ready RPC or HTTP client produces the same DTO on the server and browser. In the shared contract module, import `Schema` from `effect` and declare the DTO: @@ -30,14 +30,13 @@ const ProfileDto = Schema.Struct({ The [complete DTO recipe](https://github.com/ueberBrot/effect-api-query/blob/main/tests/types/docs-hydration-dto.ts) connects this schema to both factories and provides `snapshotDto` and `hydrateDto`. -Prefetch with `queryClient.query(dtoRpcOptions(rpcClient))` or the HTTP equivalent, then publish -`snapshotDto(queryClient)` through your framework's JSON transport. Call `hydrateDto` on the -browser's Query Client before its first query. The 60-second freshness window avoids an immediate -duplicate read; subsequent invalidation and refetch still produce DTOs. +Prefetch with `queryClient.query(dtoRpcOptions(rpcClient))` or the HTTP equivalent. Publish +`snapshotDto(queryClient)` through your framework's JSON transport. Before the browser's first +query, call `hydrateDto` on its Query Client. The 60-second freshness window avoids an immediate +duplicate read. Later invalidation and refetch still produce DTOs. If your domain layer uses classes, convert them to this DTO in the operation or ready-client -adapter on both sides. TanStack's `select` changes an observer's result; it does not change cached -query data. +adapter on both sides. TanStack's `select` changes an observer's result. It does not change cached query data. ## Reconstruct Schema classes with paired codecs @@ -66,31 +65,31 @@ In the server snapshot and browser hydration modules, pair The [complete rich-value recipe](https://github.com/ueberBrot/effect-api-query/blob/main/tests/types/docs-hydration-rich.ts) provides `snapshotProfile`, `hydrateProfile`, and options for both factories. -Prefetch with `profileRpcOptions` or `profileHttpOptions`, publish `snapshotProfile`, then call +Prefetch with `profileRpcOptions` or `profileHttpOptions`. Publish `snapshotProfile`, then call `hydrateProfile` on the browser's Query Client. The hydrated value and a later browser refetch -both have the `Profile` prototype, bigint credits, and byte array. Only the snapshot contains the -encoded strings. The `'profile'` prefix in this recipe is reserved for unary `Profile` data; -other query shapes need their own codec selection. +both have the `Profile` prototype, bigint credits, and byte array. Only the snapshot contains +encoded strings. This recipe reserves the `'profile'` prefix for unary `Profile` data. +Other query shapes need their own codec selection. `serializeData` and `deserializeData` are synchronous callbacks. The synchronous Schema functions -must be able to finish without asynchronous work or required services. A codec failure throws; -handle it through your rendering or hydration error path. +must finish without asynchronous work or required services. A codec failure throws. +Handle the failure through your rendering or hydration error path. -`structuredClone` is not a paired JSON codec. It copies class properties without restoring the -class prototype or methods. It preserves bigint and typed arrays in memory, but JSON still -requires an encoding for those values. Cloning a server class alone also leaves hydration and a -later class-valued refetch with different representations. +`structuredClone` copies class properties without restoring the class prototype or methods. +It preserves bigint and typed arrays in memory, but JSON still requires an encoding for those +values. It does not provide a paired JSON codec. If you only clone the server class, hydration +and a later class-valued refetch use different representations. ## Finish effectful preparation before publishing or hydrating -When a codec needs services or asynchronous work, prepare the entire snapshot outside TanStack's +If a codec needs services or asynchronous work, prepare the entire snapshot outside TanStack's synchronous callbacks. This example adds a caller-owned preparation service to the same Profile codec. Its hooks can await application work before encoding or decoding. The application's hydration module decodes all entries before changing the cache. -With `PreparedProfile` and its services from the -[complete asynchronous recipe](https://github.com/ueberBrot/effect-api-query/blob/main/tests/types/docs-hydration-async.ts), -that boundary is: +Use `PreparedProfile` and its services from the +[complete asynchronous recipe](https://github.com/ueberBrot/effect-api-query/blob/main/tests/types/docs-hydration-async.ts) +to define that boundary: ```ts const prepareProfileHydration = Effect.fnUntraced(function* ( @@ -109,16 +108,17 @@ const prepareProfileHydration = Effect.fnUntraced(function* ( Provide `HydrationPreparation` through your application Context or ManagedRuntime. Await the result of `prepareProfileSnapshot` before publishing its JSON. On the browser, await -`prepareProfileHydration` before allowing queries or components to read the hydrated cache. The -latter decodes every entry before calling `hydrate`, so decoding failure leaves the destination -cache untouched. Keep the preparation runtime alive until the Effect completes, and dispose it +`prepareProfileHydration` before queries or components read the hydrated cache. The hydration +function decodes every entry before calling `hydrate`. If decoding fails, the destination cache +stays untouched. + +Keep the preparation runtime alive until the Effect completes. Dispose it through the application's lifecycle. Neither preparation function puts an Effect or Promise into query data. The server cache keeps -`Profile` values, the published snapshot contains encoded data, and the browser cache receives -fully decoded `Profile` values. The Schema codec validates each profile; the surrounding snapshot -must come from the matching application encoder. Query keys and metadata must also satisfy your -JSON transport. +`Profile` values. The published snapshot contains encoded data, and the browser cache receives +fully decoded `Profile` values. The Schema codec validates each profile. The surrounding snapshot +must come from the matching application encoder. Query keys and metadata must also satisfy your JSON transport. ## Omit failed queries by default @@ -128,11 +128,13 @@ refetch them with its own client and receive a fresh `EffectRpcQueryError` or profile prefix. If you deliberately transfer failed queries, define a safe application error representation. -Encode and decode both `state.error` and `state.fetchFailureReason`; `serializeData` and -`deserializeData` transform query data, not those error fields. Allowlist public failure tags and -fields, omit private messages, defects, and transport details, and reconstruct the error behavior -your UI requires. JSON serialization of an Error or Cause does not supply that policy. A safe -error DTO will not automatically become either package error class or pass its type guard. +Encode and decode both `state.error` and `state.fetchFailureReason`. `serializeData` and +`deserializeData` transform query data without transforming those error fields. Allowlist public +failure tags and fields. Omit private messages, defects, and transport details. Reconstruct the +error behavior your UI requires. + +JSON serialization of an Error or Cause does not supply that +policy. A safe error DTO does not automatically become either package error class or pass its type guard. For normal retry and error handling, keep failures local to each client and follow [Handle Failures](/effect-api-query/guides/handle-failures/) and diff --git a/apps/docs/src/content/docs/guides/mutations.md b/apps/docs/src/content/docs/guides/mutations.md index 9008856..9137bc0 100644 --- a/apps/docs/src/content/docs/guides/mutations.md +++ b/apps/docs/src/content/docs/guides/mutations.md @@ -5,12 +5,12 @@ description: Build typed RPC and HTTP writes and refresh affected queries. Use your ready utility tree and QueryClient from the [RPC quick start](/effect-api-query/getting-started/quick-start/) or [HTTP quick start](/effect-api-query/getting-started/http-quick-start/). -The examples assume `users.create` accepts a `name` and returns a user with `id` and `name`. +In these examples, `users.create` accepts a `name` and returns a user with `id` and `name`. Import `MutationObserver` from `@tanstack/query-core` for imperative execution. ## Execute a write -Build options once, then supply each mutation's variables when it runs: +Build the options once. Supply each mutation's variables when it runs: ```ts const createUser = new MutationObserver( @@ -40,10 +40,10 @@ execution errors. Mutation keys identify the operation and exclude variables. Su ## Choose cache updates -Writes refresh reads through your callback policy. Invalidate affected generated prefixes, +Choose how your callbacks refresh reads after a write. Invalidate affected generated prefixes, seed a known result with `setQueryData`, or apply an [optimistic write](/effect-api-query/guides/optimistic-writes/). -The library derives builders from the contract; the call site chooses read or write semantics. +The library derives builders from the contract. The call site chooses read or write semantics. Buffered multipart endpoints support mutations only. See [HTTP uploads](/effect-api-query/guides/http-queries-and-mutations/#upload-a-file), diff --git a/apps/docs/src/content/docs/guides/optimistic-writes.md b/apps/docs/src/content/docs/guides/optimistic-writes.md index dec7f55..01260ee 100644 --- a/apps/docs/src/content/docs/guides/optimistic-writes.md +++ b/apps/docs/src/content/docs/guides/optimistic-writes.md @@ -3,8 +3,8 @@ title: Optimistic User Writes description: Seed typed entries, roll back individual writes, and reconcile overlapping mutations. --- -Use generated concrete keys to update the reads affected by a mutation. Keep the write workflow -in the application, where the relationship between a domain operation and its reads is known. +Use generated concrete keys to update reads affected by a mutation. Define the write workflow in +your application, which knows how a domain operation relates to its reads. The Vite React example applies this workflow to its existing user forms and directory. Copy [the user-write module](https://github.com/ueberBrot/effect-api-query/blob/main/examples/vite-react/src/lib/user-writes.ts) @@ -23,9 +23,9 @@ list with the returned `User`. It also seeds these concrete detail keys: The locale is part of the read input. A response with locale `fr` does not satisfy a detail query whose constructor supplies the default locale `en`. -Use the generated DataTag keys directly with `getQueryData` and `setQueryData`. QueryClient owns -the hash function, including global and prefix defaults; the generated keys also serve native -cancellation and invalidation. Do not hash the keys independently. +Use generated DataTag keys directly with `getQueryData` and `setQueryData`. QueryClient owns the +hash function, including global and prefix defaults. The generated keys also support native +cancellation and invalidation. Do not hash keys independently. ## Apply and reconcile an optimistic write @@ -40,13 +40,13 @@ The example's mutation options perform these actions: 6. Invalidate both user branches when the final overlapping write settles. Mutation responses remain ordinary Effect success values. An optimistic rollback restores the -application's cache; it cannot undo work the server has accepted. +application's cache. It cannot undo work the server has accepted. ## Preserve infinite pages The example updates its existing four-user page views through their generated infinite keys. It retains every page, `pageParams`, and unrelated user. A failed deletion restores only the -removed user at its former page position, preserving users created in the meantime. +removed user at its former page position. Users created in the meantime remain in the cache. Creation adjusts the total count on cached pages. It appends the new user to the last loaded page only when that page has no next cursor. A partial directory waits for reconciliation to determine @@ -55,25 +55,26 @@ where the user belongs. Each reconstructed page retains the `UserPage` represent Offset pagination can change after deletion or insertion. Preserving the current page structure keeps the optimistic view usable until settlement invalidation refreshes the server's page boundaries and cursors. Add concrete keys for any other page sizes or filters your application -uses; this example registers only its existing page size and initial cursor. +uses. This example registers only its existing page size and initial cursor. ## Keep overlapping writes safe -A full snapshot rollback can erase a later successful write. The example instead removes a -failed creation's own temporary user, or restores one deleted user without replacing the list. +A full snapshot rollback can erase a later successful write. The example removes a failed +creation's own temporary user or restores one deleted user without replacing the list. Overlapping deletions retain their original relative order, even when their failures finish in a different order. Rollback places the removed user beside surviving original neighbors and keeps newly created users. + An authoritative cache update supersedes the optimistic revision, even if its data is unchanged. -A failed deletion leaves that newer update intact. -Deletions started after a refresh capture the current user and position for rollback. -Overlapping deletions of the same user share the first snapshot: one successful deletion prevents -an older failure from restoring it, while all failed deletions restore the original entity once. +A failed deletion leaves that newer update intact. Deletions started after a refresh capture the +current user and position for rollback. Overlapping deletions of the same user share the first +snapshot. One successful deletion prevents an older failure from restoring the user. +If all deletions fail, rollback restores the original entity once. Each new write cancels an earlier reconciliation fetch before publishing its optimistic change. -Only the final pending write triggers settlement refetches, so an older settlement does not -refresh over a newer optimistic result. Success callbacks also cancel reads before seeding the -returned value. Native cancellation prevents a cancelled query from publishing a late result. +Only the final pending write triggers refetches on settlement. An older settlement therefore does +not refresh over a newer optimistic result. Success callbacks also cancel reads before seeding +the returned value. Native cancellation prevents a cancelled query from publishing a late result. All writes to these views must participate in the same application workflow. Background domain refreshes, manual cache replacement, and server-side write conflicts need their own coordination. @@ -82,15 +83,15 @@ The example does not infer server ordering or define a general conflict-resoluti ## Retain the original owner The `onMutate` result carries the original owner's commit, rollback, and settlement actions. -Callbacks invoke those actions and check that owner is active. A late response can neither refill -a cleared retired cache nor seed or invalidate a replacement client's entries. +Callbacks invoke those actions and check that the owner is active. A late response cannot refill +a cleared retired cache, seed a replacement client's entries, or invalidate those entries. Native MutationObserver option updates can replace callbacks on a pending mutation. Carrying the -transaction in `onMutate` prevents a later callback closure from redirecting an already-started -write. Remount mutation consumers when replacing an application owner: an options change while -`onMutate` awaits cancellation can otherwise replace the mutation function before it starts. +transaction in `onMutate` prevents a later callback closure from redirecting a write already in +progress. When replacing an application owner, remount mutation consumers. An options change +while `onMutate` awaits cancellation can replace the mutation function before it starts. Track mutation-function completion before releasing the application's ready clients. These writes use `networkMode: 'always'` and `retry: false`, so a retired owner does not retain paused -mutation work. They do not use a native mutation scope queue. Mutations have no query abort signal; -retirement and cache clearing do not cancel an accepted server write. +mutation work. They do not use a native mutation scope queue. Mutations have no query abort signal. +Retirement and cache clearing do not cancel an accepted server write. diff --git a/apps/docs/src/content/docs/guides/pagination.md b/apps/docs/src/content/docs/guides/pagination.md index 28d48d5..9ad671a 100644 --- a/apps/docs/src/content/docs/guides/pagination.md +++ b/apps/docs/src/content/docs/guides/pagination.md @@ -5,7 +5,7 @@ description: Map page parameters to typed RPC payloads and HTTP requests. Use a ready [RPC](/effect-api-query/getting-started/quick-start/) or [HTTP](/effect-api-query/getting-started/http-quick-start/) utility tree. -These examples assume `users.page` accepts numeric `cursor` and `pageSize` fields and returns +In these examples, `users.page` accepts numeric `cursor` and `pageSize` fields and returns `{ users, nextCursor }`, with `null` marking the last page. ## Build page options @@ -32,17 +32,17 @@ const httpPages = http.users.page.infiniteOptions({ ``` Pass these options to the native infinite-query hook to load further pages with `fetchNextPage`. -Inputless operations omit `input`. Input-bearing operations can pause with `input: skipToken`; -see [Conditional Queries](/effect-api-query/guides/conditional-queries/). +Inputless operations omit `input`. Input-bearing operations can pause with `input: skipToken`. +See [Conditional Queries](/effect-api-query/guides/conditional-queries/). ## Keep page identity stable -The mapped initial request determines cache identity. Keep filters in that request and every -page; rebuild options when filters change. The mapper runs during key construction and page +The mapped initial request determines cache identity. Include filters in that request and every +page. When filters change, rebuild the options. The mapper runs during key construction and page execution, so keep it deterministic. -TanStack stores `pages` and `pageParams`. A native `select` can flatten returned data while cache -reads retain the original pages and inferred page-parameter type: +TanStack stores `pages` and `pageParams`. A native `select` can flatten returned data. Cache reads +retain the original pages and inferred page-parameter type: ```ts const cachedPages = queryClient.getQueryData(httpPages.queryKey) diff --git a/apps/docs/src/content/docs/guides/queries.md b/apps/docs/src/content/docs/guides/queries.md index 6e68904..38a6503 100644 --- a/apps/docs/src/content/docs/guides/queries.md +++ b/apps/docs/src/content/docs/guides/queries.md @@ -3,9 +3,9 @@ title: Queries description: Build typed query options for cached RPC and HTTP reads. --- -Start with a ready utility tree and QueryClient from the [RPC quick start](/effect-api-query/getting-started/quick-start/) +Use the ready utility tree and QueryClient from the [RPC quick start](/effect-api-query/getting-started/quick-start/) or [HTTP quick start](/effect-api-query/getting-started/http-quick-start/). -Here, `users.get` returns a user with `id` and `name`; its RPC payload has an `id` and default locale. +In this example, `users.get` returns a user with `id` and `name`. Its RPC payload has an `id` and default locale. The HTTP endpoint declares `params.id` and `query.locale`. ## Fetch decoded data @@ -28,12 +28,12 @@ const httpUser = await queryClient.query(httpUserOptions) ``` These options also work with native query, suspense, and prefetch hooks. An inputless operation -uses `queryOptions()`. Queries cache decoded success, with top-level `undefined` converted to `null`. +uses `queryOptions()`. Queries cache decoded success and convert top-level `undefined` to `null`. Keep captured input values unchanged while using the options. ## Select returned data -Native options such as `staleTime`, `retry`, and `select` pass through: +Pass native options such as `staleTime`, `retry`, and `select` to the builder: ```ts const nameOptions = rpc.users.get.queryOptions({ @@ -45,7 +45,7 @@ const cachedUser = queryClient.getQueryData(nameOptions.queryKey) ``` Hooks, observers, and `queryClient.query` return the selected name. The cache and `getQueryData` -retain the decoded user; generated keys preserve that type. Builders supply `queryKey` and `queryFn`. +retain the decoded user. Generated keys preserve that type. Builders supply `queryKey` and `queryFn`. Configure hashing through [QueryClient defaults](/effect-api-query/guides/query-defaults/). Continue with [Conditional Queries](/effect-api-query/guides/conditional-queries/), diff --git a/apps/docs/src/content/docs/guides/query-defaults.md b/apps/docs/src/content/docs/guides/query-defaults.md index d74d994..996d17a 100644 --- a/apps/docs/src/content/docs/guides/query-defaults.md +++ b/apps/docs/src/content/docs/guides/query-defaults.md @@ -3,17 +3,17 @@ title: Defaults and Policies description: Reuse native cache defaults and typed query or mutation policies. --- -Choose where each setting belongs, then reuse it across generated builders: +Choose where to define each setting before reusing it across generated builders: | Scope | Use | Suitable settings | | ------------------------ | ------------------------------------------- | --------------------------------------------------------------- | | Cache owner or namespace | QueryClient defaults and generated prefixes | Common freshness, retry, garbage collection, and hashing | -| One leaf and query view | `withQueryPolicy` | Freshness and typed data/error callbacks | +| One leaf and query view | `withQueryPolicy` | Freshness and typed data and error callbacks | | One mutation leaf | `withMutationPolicy` | Typed invalidation and other mutation callbacks | | One call | The options builder | Input, selection, initial data, pagination, and local overrides | Use your existing utility tree and application-owned QueryClient. Policies preserve the original -builder's inference; the library continues to supply its keys and execution functions. +builder's inference. The library supplies the generated keys and execution functions. ## Register native defaults @@ -38,8 +38,8 @@ queryClient.setQueryDefaults(viewPrefix(rpc.users.get, 'query'), { staleTime: 12 ``` TanStack merges global defaults, matching registered prefixes in registration order, and explicit -builder options. It does not sort prefixes by specificity. Here `users.get` inherits -`gcTime: 600_000`, `retry: 2`, and `staleTime: 120_000`; two retries allow at most three executions. +builder options. It does not sort prefixes by specificity. Here, `users.get` inherits +`gcTime: 600_000`, `retry: 2`, and `staleTime: 120_000`. Two retries allow at most three executions. `rpc.key()` covers the tree, `rpc.users.key()` covers its branch, and the view prefix covers only ordinary queries of that leaf, for every input. HTTP uses the same native APIs: @@ -53,14 +53,14 @@ This five-second policy applies to metadata without changing the ordinary view's Register mutation defaults separately with native `setMutationDefaults` and `mutationKey()`. See [Cache Keys and Filters](/effect-api-query/guides/cache-filters/) for prefix scopes. -Configure hashing through QueryClient defaults before using the cache, and keep it stable for the -cache owner's lifetime. Builders reject per-call hashing overrides; see +Before using the cache, configure hashing through QueryClient defaults. Keep hashing stable for +the cache owner's lifetime. Builders reject per-call hashing overrides. See [Semantic Keys](/effect-api-query/concepts/semantic-keys/) for the identity contract. ## Bind a reusable query policy -Use `withQueryPolicy` when a policy belongs to one leaf and view. `QueryPolicy` gives reusable -objects that leaf's native option types; retry and refetch callbacks retain its data and error types. +If a policy belongs to one leaf and view, use `withQueryPolicy`. `QueryPolicy` types reusable +objects with that leaf's native options. Retry and refetch callbacks retain its data and error types. Import both from `effect-api-query`: ```ts @@ -85,8 +85,8 @@ Policies accept applicable native freshness, retry, refetch, and cache settings. call site. Stream retention and decoder policies determine cache identity and also stay local. A bound policy supplies explicit builder options, so it overrides QueryClient defaults. Call-site -options override the bound policy. Omit a field to inherit it; use `staleTime: 0` or `retry: false` -to replace an inherited setting. +options override the bound policy. To inherit a field, omit it. To replace an inherited setting, +use a value such as `staleTime: 0` or `retry: false`. ## Bind mutation invalidation @@ -103,8 +103,8 @@ const createUserOptions = withMutationPolicy(rpc.users.create, { ``` Shared callbacks infer data, variables, and execution errors. `MutationPolicy` can annotate a -reusable object. Its optimistic result is `unknown`: supply `onMutate` at each call site to infer -that result in local callbacks. Here `reportCreationFailure` is the application's failure notifier: +reusable object. Its optimistic result is `unknown`. To infer that result in local callbacks, +supply `onMutate` at each call site. Here, `reportCreationFailure` notifies the application of a failure: ```ts const createUser = createUserOptions({ diff --git a/apps/docs/src/content/docs/guides/react-query.md b/apps/docs/src/content/docs/guides/react-query.md index e2d15f0..bd4f2c2 100644 --- a/apps/docs/src/content/docs/guides/react-query.md +++ b/apps/docs/src/content/docs/guides/react-query.md @@ -3,14 +3,14 @@ title: React Query description: Acquire a ready client and use generated options with native React Query hooks. --- -Pass generated options directly to React Query's native hooks. Build the utility tree once for -its ready client and cache owner. Rebuild options when component input changes; the builders -capture plain input for that execution. +Pass generated options to React Query's native hooks. Build the utility tree once for its ready +client and cache owner. When component input changes, rebuild the options. Builders capture +plain input for each execution. ## Acquire and mount the client -Keep the shared contract, client lifetime, and React UI in separate modules. Mount the application -once from its entry point. +Put the shared contract, client lifetime, and React UI in separate modules. Mount the application +once from the entry point. This read-only example requires React, React DOM, `@tanstack/react-query`, Effect, and `effect-api-query`. The server must implement the shared contract below: `GET /users/:id` @@ -20,7 +20,7 @@ returns `{ id: number, name: string }`. Place `usersApi` in `http-contract.ts`. Both the server and client import this module. -**Shared contract — `http-contract.ts`** +Shared contract in `http-contract.ts`: ```ts import { Schema } from 'effect' @@ -38,10 +38,10 @@ export const usersApi = HttpApi.make('users-api').add( ### Client lifetime -In `client.ts`, `startUsersApplication` acquires the ready HTTP API client and keeps one runtime -and QueryClient alive until `dispose` runs. Export its application type for the UI module. +In `client.ts`, `startUsersApplication` acquires the ready HTTP API client. It keeps one runtime +and QueryClient alive until `dispose` runs. Export the application type for the UI module. -**Client integration — `client.ts`** +Client integration in `client.ts`: ```ts import { QueryClient } from '@tanstack/react-query' @@ -98,16 +98,16 @@ export const startUsersApplication = async (baseUrl: string) => { export type UsersApplication = Awaited> ``` -The supplied runner forwards TanStack's abort options and tracks in-flight reads. After cancellation, -disposal waits for their Effect finalizers before releasing the runtime. Client acquisition finishes -before React renders. Acquisition failures also dispose the runtime. +The supplied runner forwards TanStack's abort options and tracks reads in progress. After +cancellation, disposal waits for their Effect finalizers before releasing the runtime. +Client acquisition finishes before React renders. If acquisition fails, the application disposes the runtime. ### Component and provider In `users-app.tsx`, import the client setup and its application type. The component receives the -ready utility tree through props; `QueryClientProvider` exposes the matching cache to React Query. +ready utility tree through props. `QueryClientProvider` exposes the matching cache to React Query. -**React UI — `users-app.tsx`** +React UI in `users-app.tsx`: ```tsx import { QueryClientProvider, useQuery } from '@tanstack/react-query' @@ -147,7 +147,7 @@ export const mountUsersApplication = async (element: HTMLElement, baseUrl: strin In `main.tsx`, mount into the root element supplied by your HTML page. Replace the base URL with your server's URL. -**Application entry point — `main.tsx`** +Application entry point in `main.tsx`: ```ts import { mountUsersApplication } from './users-app.tsx' @@ -160,10 +160,9 @@ if (element === null) { export const stopUsers = await mountUsersApplication(element, 'http://localhost:3000') ``` -The host calls and awaits `stopUsers()` when tearing down this application; -it unmounts React, cancels outstanding reads, awaits their local finalizers, clears the cache, -and disposes the runtime. -A browser's unload event cannot guarantee that async cleanup finishes. +When tearing down the application, the host calls and awaits `stopUsers()`. That function +unmounts React, cancels outstanding reads, awaits their local finalizers, clears the cache, and +disposes the runtime. A browser's unload event cannot guarantee that asynchronous cleanup finishes. This example has one buffered read. Before adding open streams or concurrent writes, use [Client Lifecycle](/effect-api-query/concepts/client-lifecycle/) to await their cleanup and settlement. @@ -174,15 +173,15 @@ tree to the component. The provider and React hooks stay the same. ## Choose the native hook The provider and component above use a buffered HTTP query. RPC uses the same hooks with its -[constructor input](/effect-api-query/concepts/data-normalization/#query-stable-rpc-payloads); +[constructor input](/effect-api-query/concepts/data-normalization/#query-stable-rpc-payloads). HTTP takes [decoded request input](/effect-api-query/concepts/data-normalization/#decoded-http-inputs). -| Native consumer | Generated options | Cached representation | -| ------------------ | -------------------------------------- | ------------------------------------------------------ | -| `useQuery` | `queryOptions`, HTTP `metadataOptions` | Decoded data, or decoded data with status and headers | -| `useInfiniteQuery` | `infiniteOptions` | Pages with their page parameters | -| `useQuery` | `streamedOptions`, `liveOptions` | Retained history, or the latest emitted value | -| `useMutation` | `mutationOptions` | Mutation result; variables supplied when `mutate` runs | +| Native consumer | Generated options | Cached representation | +| ------------------ | -------------------------------------- | ----------------------------------------------------------- | +| `useQuery` | `queryOptions`, HTTP `metadataOptions` | Decoded data, or decoded data with status and headers | +| `useInfiniteQuery` | `infiniteOptions` | Pages with their page parameters | +| `useQuery` | `streamedOptions`, `liveOptions` | Retained history, or the latest emitted value | +| `useMutation` | `mutationOptions` | Mutation result, with variables supplied when `mutate` runs | Pass `select`, `staleTime`, retry policy, and other native options through the builder. `select` changes observer data while the cache retains the generated view's query data. @@ -196,9 +195,9 @@ Use [Defaults and Policies](/effect-api-query/guides/query-defaults/) to bind an policy once, then reuse it in components, imperative queries, or route loaders. A call-site projection or callback keeps its native inference. -For writes, pass generated mutation options to `useMutation` and supply variables through -`mutate`. Choose the affected generated keys in the application: writes do not invalidate reads -automatically. Use [Cache Keys and Filters](/effect-api-query/guides/cache-filters/) for broad or exact +For writes, pass generated mutation options to `useMutation`. Supply variables through `mutate`. +Choose which generated keys the write affects. Writes do not invalidate reads automatically. +Use [Cache Keys and Filters](/effect-api-query/guides/cache-filters/) for broad or exact invalidation and [Optimistic Updates](/effect-api-query/guides/optimistic-writes/) for response seeding, rollback, and overlapping writes. @@ -211,7 +210,7 @@ clients alive until local iterator finalizers finish. TanStack documents that cancellation does not work with its Suspense hooks. Use ordinary query hooks when cancelling reads or stopping open stream work is part of the screen's lifecycle. For buffered reads with Suspense, use -[Suspense and Error Recovery](/effect-api-query/guides/suspense-and-recovery/) for reset/retry, +[Suspense and Error Recovery](/effect-api-query/guides/suspense-and-recovery/) for reset and retry, cached-data refetch failures, and independent loading with `useSuspenseQueries`. For router loaders and server rendering, continue with diff --git a/apps/docs/src/content/docs/guides/refresh-from-events.md b/apps/docs/src/content/docs/guides/refresh-from-events.md index 4da4635..564b746 100644 --- a/apps/docs/src/content/docs/guides/refresh-from-events.md +++ b/apps/docs/src/content/docs/guides/refresh-from-events.md @@ -3,14 +3,13 @@ title: Refresh Reads from Domain Events description: Map domain delivery and Effect Reactivity to scoped native Query invalidation. --- -Use one application-owned consumer to map a domain event to the reads it changes. The mapping -must name both adapters when RPC and HTTP represent the same domain: their generated cache roots -remain separate. +Use one application-owned consumer to map each domain event to the reads it changes. If RPC and +HTTP represent the same domain, name both adapters in the mapping. Their generated cache roots remain separate. Copy [the user-event consumer](https://github.com/ueberBrot/effect-api-query/blob/main/tests/fixtures/user-events.ts) -next to your owner, write, and client modules, adjusting its local application imports. Its inputs are the captured owner and -an application-owned Effect Reactivity service. Supply your own decoded network stream; the module -creates no connection, broker, provider, or second cache. +next to your owner, write, and client modules. Adjust its local application imports. The consumer +takes the captured owner and an application-owned Effect Reactivity service. Supply your own +decoded network stream. The module creates no connection, broker, provider, or second cache. ## Map events to reads @@ -28,15 +27,15 @@ Generated keys and native filters retain the owning QueryClient's global and pre The mapping depends on the domain relationship, so the package does not infer it from mutation or event names. -The `ownerKey` contains safe tenant, user, session, and permission-generation identity. It is a -routing partition, not authentication. The application must authenticate the transport and -validate which owner may receive each event. +The `ownerKey` contains safe tenant, user, session, and permission-generation identity. It +partitions event routing. The application must separately authenticate the transport and validate +which owner can receive each event. ## Register one consumer for the owner -`makeUserEventConsumer`, `decodeUserEvent` and `flush` are application helpers from the linked -consumer module. Import `Effect` and `Stream` from `effect`; provide the captured `owner` and -application `reactivity` service. Inside the owner's Effect Scope, acquire once: +`makeUserEventConsumer`, `decodeUserEvent`, and `flush` are application helpers from the linked +consumer module. Import `Effect` and `Stream` from `effect`. Provide the captured `owner` and +application `reactivity` service. Acquire the consumer once inside the owner's Effect Scope. Acquire `consumer` with `yield* makeUserEventConsumer(owner, reactivity, { observedUsers: [{ id: 1, locale: 'en' }] })`. This registers the owner's selected local detail mappings. @@ -54,48 +53,50 @@ const consume = (events: Stream.Stream) => Run `consume(events)` with the application's runtime. The [complete typed attachment](https://github.com/ueberBrot/effect-api-query/blob/main/tests/types/docs-user-events.ts) -shows composition. It rejects inactive/duplicate owners and unregisters listeners on Scope closure. +shows composition. It rejects inactive or duplicate owners and unregisters listeners when the Scope closes. A retired consumer ignores late delivery and cannot redirect it to a replacement QueryClient. -Close its Scope, cancel/drain started reads, then release clients before replacing the owner. +Close the consumer's Scope. Cancel and drain started reads. Release the clients before replacing the owner. See [Switch Cache Owners](/effect-api-query/guides/switch-cache-owners/). ## Coalesce local invalidation Use the consumer's `reactivityKeys.directory`, `reactivityKeys.all`, or `reactivityKeys.user({ id, locale })` with Effect Reactivity. Exact local user mappings exist for -the `observedUsers` registered when the consumer starts; include every detail read your local +the `observedUsers` registered when the consumer starts. Include every detail read your local writers must refresh. Delivered domain events can target any valid user. -These keys are stable application strings. Reactivity hashes its own keys; a TanStack query tuple -has no implied Reactivity subscription or prefix relationship. Register the deliberate mapping -rather than passing generated tuples to both systems. +These keys are stable application strings. Reactivity hashes its own keys. A TanStack query tuple +has no implied Reactivity subscription or prefix relationship. Register an explicit mapping +between the two systems. -The consumer coalesces invalidation intent and lets broad refreshes subsume narrow work. -It defers refresh while its captured QueryClient has pending mutations, protecting optimistic -writes; other refresh paths need the same application policy. `flush()` drains runnable batches, -leaving intent queued during pending writes. Refresh-job errors go to `onRefreshError`; query -errors retain native Query retry/state behavior. See the linked consumer for its bookkeeping +The consumer combines invalidation requests, and broad refreshes cover narrower work. +It defers refresh while its captured QueryClient has pending mutations. This protects optimistic +writes. Other refresh paths need the same application policy. `flush()` drains runnable batches +and leaves requests queued during pending writes. Refresh-job errors go to `onRefreshError`. +Query errors retain native Query retry and state behavior. + +See the linked consumer for its bookkeeping bounds and [Optimistic User Writes](/effect-api-query/guides/optimistic-writes/) for overlap rules. ## Resume delivery deliberately This recipe assumes one ordered, monotonically increasing cursor sequence per owner. It ignores cursors already accepted. A gap queues a full user-branch refresh and advances the accepted cursor. -The cursor records accepted invalidation intent; it is not a durable acknowledgement or a claim -that a read succeeded. +The cursor records accepted invalidation requests. It does not provide a durable acknowledgement +or establish that a read succeeded. Reconnect using `consumer.cursor()` as the last accepted cursor. Call `consumer.resume(true)` when the transport can replay subsequent events in order. Replayed duplicates remain harmless. If -replay is unavailable, call `consumer.resume(false)` before accepting the new sequence: it resets -the cursor and refreshes both user branches. Stop delivery from the old connection first. +replay is unavailable, call `consumer.resume(false)` before accepting the new sequence. That call +resets the cursor and refreshes both user branches. Stop delivery from the old connection first. The application owns reconnect backoff, replay retention, cursor storage, source epochs, schema migration, authorization, acknowledgements, and out-of-order delivery handling. A new owner Scope -starts at cursor zero. Fetch its authoritative reads on startup; this recipe does not promise +starts at cursor zero. Fetch its authoritative reads on startup. This recipe does not promise durable or exactly-once delivery. Invalid envelopes fail Schema decoding before changing the cursor. ## Choose a visible stream view Use a live query for complete snapshots or bounded history for recent events. The latest delta alone does not reconstruct state. An indefinitely open `replace` stream does not publish a -replacement before completion; see [Choose Stream History](/effect-api-query/guides/choose-stream-history/). +replacement before completion. See [Choose Stream History](/effect-api-query/guides/choose-stream-history/). diff --git a/apps/docs/src/content/docs/guides/retry-queries.md b/apps/docs/src/content/docs/guides/retry-queries.md index 0e5da3c..d04f2f5 100644 --- a/apps/docs/src/content/docs/guides/retry-queries.md +++ b/apps/docs/src/content/docs/guides/retry-queries.md @@ -3,13 +3,13 @@ title: Retry Queries description: Reuse bounded read policies with typed Effect failures and native Query options. --- -Bind a retry policy once with `withQueryPolicy`, then call the returned options builder wherever -you need that read. Each call keeps native `select`, input, and initial-data inference. Call-site -options replace matching defaults. +Bind a retry policy with `withQueryPolicy`. Use the returned options builder wherever you need +that read. Each call keeps native `select`, input, and initial-data inference. Call-site options +replace matching defaults. -The policy below permits two retries after the initial failure: at most three executions. It -allows a declared `RetryLater` or an application-approved transport failure. Use it for idempotent -reads whose transport does not already own retries. +The policy below permits two retries after the initial failure, for at most three executions. +It allows a declared `RetryLater` or an application-approved transport failure. +Use it for idempotent reads whose transport does not already own retries. ## Share the decision and delay @@ -51,8 +51,8 @@ const retryPolicy = ( }) ``` -This rejects defects, interruptions, and Causes with multiple reasons. `Cause.findError` returns -a `Result`; the successful value retains the typed failure union. Schema refinement also handles +The helper rejects defects, interruptions, and Causes with multiple reasons. `Cause.findError` +returns a `Result`. The successful value retains the typed failure union. Schema refinement also handles unknown values safely. The adapter guard rejects custom runner failures that pass through unchanged. ## Bind a typed RPC policy @@ -82,8 +82,8 @@ belongs to the application that owns those transports. ## Bind the same decision to HTTP -Import `isEffectHttpApiQueryError` from `effect-api-query` and `HttpClientError` from `effect/http`. Keep the shared retry -logic and supply HTTP's transport classifier: +Import `isEffectHttpApiQueryError` from `effect-api-query` and `HttpClientError` from `effect/http`. +Reuse the shared retry logic with HTTP's transport classifier: ```ts type Error = InferQueryError @@ -116,12 +116,12 @@ for policy types, mutation callbacks, and precedence. | Mutation | Retries disabled | QueryClient global and prefix defaults still apply. Set `retry` explicitly when attempt counts -matter. `retry: 2` permits three executions; `retry: false` permits one. +matter. `retry: 2` permits three executions. `retry: false` permits one. Choose one layer to own retries. Two retries in TanStack combined with two retries per invocation in Effect can make nine client attempts. A retry around iterable creation does not retry later -stream consumption. Restart a stream only when safe for that operation; retention and refetch policy -still govern cache publication. Mutation retries require application idempotency. +stream consumption. Restart a stream only when restarting is safe for that operation. Retention and refetch +policies still govern cache publication. Mutation retries require application idempotency. `retryDelay` receives a zero-based attempt index and returns milliseconds. Query Core can evaluate it for terminal failures, so keep it pure. This recipe clamps server delays and exponential backoff @@ -129,13 +129,13 @@ to 30 seconds. ## Handle preparation before execution -Factory, builder, and key-generation errors are synchronous. Prepare options inside `try` when -input or custom encoders may fail, then pass successful options to Query Core. Preparation failures -never enter execution retry. An infinite page-input mapper runs later; its thrown errors pass +Factory, builder, and key-generation errors are synchronous. If input or custom encoders can fail, +prepare options inside `try`. Pass successfully prepared options to Query Core. Preparation failures +never enter execution retry. An infinite page-input mapper runs later. Its thrown errors pass through unchanged. Mutation encoding occurs inside the ready client's Effect and uses its Cause. Keep QueryClient hashing stable while entries remain cached. `UnsupportedQueryHash` rejects an -incompatible stream cache policy; retries cannot repair it. Retire the cache before replacing its +incompatible stream cache policy. Retries cannot repair it. Retire the cache before replacing its hasher. See [Failure Handling](/effect-api-query/guides/handle-failures/) for error-stage guards and [Client Lifecycle](/effect-api-query/concepts/client-lifecycle/#stream-creation-and-consumption) for stream execution boundaries. diff --git a/apps/docs/src/content/docs/guides/solid-query.md b/apps/docs/src/content/docs/guides/solid-query.md index edec2ed..6752a94 100644 --- a/apps/docs/src/content/docs/guides/solid-query.md +++ b/apps/docs/src/content/docs/guides/solid-query.md @@ -3,16 +3,16 @@ title: Solid Query description: Rebuild generated options with a Solid accessor and keep client resources alive. --- -Pass generated options from either factory to Solid Query's native hooks. Configure -`QueryClientProvider` and acquire the ready client before creating the utilities. +Pass options from either factory to Solid Query's native hooks. Before creating the utilities, +configure `QueryClientProvider` and acquire the ready client. The examples target Solid 1.9.15 with Solid Query 5.104.0 and Query Core 5.104.0. ## Rebuild options when input changes -Builders capture plain input when called. Build options inside Solid's options accessor so a -changed input produces a new key and query function. Passing a builder's result once leaves the -query attached to the original input. +Builders capture plain input when you call them. Build options inside Solid's options accessor. +When input changes, the accessor creates a new key and query function. If you pass a builder's +result once, the query keeps using the original input. Keep [captured inputs unchanged](/effect-api-query/concepts/data-normalization/#keep-captured-inputs-unchanged) while their options can execute. @@ -38,20 +38,20 @@ const user = useQuery(() => { }) ``` -Read the selected value from `user.data`. Apply the same accessor pattern to `infiniteOptions`, -`streamedOptions`, `liveOptions`, and `metadataOptions` when their input changes. Use -`useMutation(() => rpc.users.write.mutationOptions())` for writes, supplying each execution's -input through `mutate(variables)`. Read reactive mutation settings inside the accessor. +Read the selected value from `user.data`. When input changes, use the same accessor pattern for +`infiniteOptions`, `streamedOptions`, `liveOptions`, and `metadataOptions`. For writes, use +`useMutation(() => rpc.users.write.mutationOptions())`. Supply each execution's input through +`mutate(variables)`. Read reactive mutation settings inside the accessor. ## Choose native options carefully Solid accepts Core's `enabled(query)` callback. Solid Query 5.104.0 query and infinite-query -overloads accept defined initial data or absent initial data. A value or initializer that may +overloads accept defined initial data or absent initial data. A value or initializer that can return `undefined` does not match those overloads. Omit `initialData` or supply a defined seed. An explicit `initialData: undefined` overrides inherited QueryClient initial data. ## Own the native integration Follow the [shared integration lifetime](/effect-api-query/integrations/#own-the-cache-and-client-lifetime) -for providers, observer removal, stream finalizers, and accepted mutations. Match this native -wrapper to the application's Query Core and the framework's supported compiler/tooling. +for providers, observer removal, stream finalizers, and accepted mutations. Match the native +wrapper to the application's Query Core. Use a compiler and tooling that Solid supports. diff --git a/apps/docs/src/content/docs/guides/stream-snapshots.md b/apps/docs/src/content/docs/guides/stream-snapshots.md index b4d06c1..6aafee9 100644 --- a/apps/docs/src/content/docs/guides/stream-snapshots.md +++ b/apps/docs/src/content/docs/guides/stream-snapshots.md @@ -4,8 +4,8 @@ description: Capture an accumulated or live stream for server rendering and hydr --- Use `fetchStreamSnapshot` when server rendering needs data from an open RPC or HTTP SSE query. -It waits for the first new cache publication or normal completion, cancels the exact query, and -awaits its local iterator cleanup. Ordinary `queryClient.query` waits for an open stream to finish. +The helper waits for the first new cache publication or normal completion. It cancels the exact +query and awaits local iterator cleanup. Ordinary `queryClient.query` waits for an open stream to finish. ## Capture data before dehydration @@ -39,11 +39,11 @@ const latest = await fetchStreamSnapshot( ) ``` -Then dehydrate successful data with your application's serializer, and dispose request resources -after cleanup. HTTP SSE accepts the same helper with its generated options. The +Dehydrate successful data with your application's serializer. After cleanup, dispose the request +resources. HTTP SSE accepts the same helper with its generated options. The [complete typed recipe](https://github.com/ueberBrot/effect-api-query/blob/main/tests/types/docs-stream-snapshot.ts) -includes contract/client composition; [Hydrate Query Views](/effect-api-query/guides/hydrate-query-views/) -covers history, live values and paired codecs. +includes the contract and client setup. [Hydrate Query Views](/effect-api-query/guides/hydrate-query-views/) +covers history, live values, and paired codecs. The default `fresh` mode starts a new attempt even when cached or initial data has a future timestamp. Old data cannot settle that attempt. Native stream policies still apply: `append` @@ -57,22 +57,24 @@ exists, cached mode starts a fresh capture. ## Keep capture ownership exclusive Use a request-local QueryClient and reserve the exact key throughout capture. A fresh capture -rejects a key that is fetching, paused, or observed. Other keys keep running. Avoid same-key writes, -observers, or concurrent fetches until capture settles: an external `setQueryData` publication can -otherwise be mistaken for the stream's first value. Configure hashing through QueryClient global -or prefix defaults; per-call `queryHash` and `queryKeyHashFn` are rejected. +rejects a key that is fetching, paused, or observed. Other keys keep running. Until capture settles, +avoid writes, observers, or concurrent fetches on that key. An external `setQueryData` publication +can be mistaken for the stream's first value. + +Configure hashing through QueryClient global or +prefix defaults. The helper rejects per-call `queryHash` and `queryKeyHashFn`. The helper waits for the supplied query function to finish local cleanup before returning or rejecting. -Dispose the server's ready client, Scope, or runtime after that settlement. This does not await a -remote server's cancellation acknowledgement. A finalizer that never finishes also prevents -settlement; `timeoutMs` limits the wait for data, not the duration of finalization. +After capture settles, dispose the server's ready client, Scope, or runtime. Capture does not await +a remote server's cancellation acknowledgement. A finalizer that never finishes prevents settlement. +`timeoutMs` limits the wait for data and does not limit finalization. ## Hydrate and reconnect in the browser Give the browser its own QueryClient, ready client, runner, and Scope. Hydrate the server snapshot -using the same safe key prefix and input, then observe the generated stream options with your -usual refetch policy. A new browser fetch reconnects with browser-owned resources; disposing the -server's resources cannot dispose those resources. +using the same safe key prefix and input. Then observe the generated stream options with your +usual refetch policy. A new browser fetch reconnects with browser-owned resources. Disposing the +server's resources cannot dispose the browser's resources. The [TanStack Start example](/effect-api-query/guides/tanstack-start/) captures server snapshots and reconnects accumulated and live views after hydration. The diff --git a/apps/docs/src/content/docs/guides/streaming.md b/apps/docs/src/content/docs/guides/streaming.md index fb82c49..83ee3ab 100644 --- a/apps/docs/src/content/docs/guides/streaming.md +++ b/apps/docs/src/content/docs/guides/streaming.md @@ -3,8 +3,8 @@ title: Streaming description: Cache accumulated or latest values from RPC streams and HTTP SSE. --- -Streaming leaves provide two views: an accumulated streamed query caches emissions in order; -a live query caches the latest value. Use ordinary query hooks or `QueryObserver` for both. +Streaming leaves provide two views. An accumulated streamed query caches emissions in order. +A live query caches the latest value. Use ordinary query hooks or `QueryObserver` for both. Start with a ready [RPC](/effect-api-query/getting-started/quick-start/) or [HTTP](/effect-api-query/getting-started/http-quick-start/) utility tree. Here, `diagnostics.stream` is an inputless RPC emitting strings, and `events.watch` is an HTTP SSE @@ -32,8 +32,10 @@ const sseHistory = http.events.watch.streamedOptions({ ``` The first emission makes the query successful while fetching continues. `maxChunks` bounds -cached elements after each emission; omitted means unlimited. It does not bound bytes or -transport buffering. Refetch mode `reset` starts a new history, `append` adds to it, and `replace` +cached elements after each emission. If you omit it, history is unlimited. It does not bound bytes +or transport buffering. + +Refetch mode `reset` starts a new history, `append` adds to it, and `replace` keeps the old history until successful completion. An indefinitely open replacement keeps showing that old history. Accumulated streams can succeed without emissions. @@ -49,19 +51,19 @@ const latestSse = http.events.watch.liveOptions({ }) ``` -Each emission replaces cached data; completion preserves it. Live queries convert top-level +Each emission replaces cached data. Completion preserves that data. Live queries convert top-level `undefined` to `null`, while accumulated elements retain `undefined`. Empty live completion raises the adapter's empty-stream error. Live options accept neither `maxChunks` nor `refetchMode`. ## Address and stop a view -Reuse the options' `queryKey` for exact cache access. Accumulated history policies and HTTP decoder -policies affect identity; live and accumulated views remain separate. Endpoint prefixes match +For exact cache access, reuse the options' `queryKey`. Accumulated history policies and HTTP decoder +policies affect identity. Live and accumulated views remain separate. Endpoint prefixes match all their views. Cancellation closes the stream iterator. Your application owns reconnection, resume policy, and client lifetime. HTTP `sseOptions.maxEventSize` bounds pending parser text in JavaScript string -code units; its default is 10 × 1024 × 1024 code units. Declared resume headers belong in `input.headers`. +code units. Its default is 10 × 1024 × 1024 code units. Put declared resume headers in `input.headers`. See [Choose Stream History](/effect-api-query/guides/choose-stream-history/) for retention and buffering, [Stream Snapshots](/effect-api-query/guides/stream-snapshots/) for finite server captures, diff --git a/apps/docs/src/content/docs/guides/suspense-and-recovery.md b/apps/docs/src/content/docs/guides/suspense-and-recovery.md index 6b5c3da..081ba43 100644 --- a/apps/docs/src/content/docs/guides/suspense-and-recovery.md +++ b/apps/docs/src/content/docs/guides/suspense-and-recovery.md @@ -3,7 +3,7 @@ title: Suspense and Error Recovery description: Reset failed Suspense queries, preserve cached data, and start independent reads together. --- -Use TanStack's native Suspense hooks inside an existing `QueryClientProvider`, such as the +Use TanStack's native Suspense hooks inside an existing `QueryClientProvider`. For example, use the [React client setup](/effect-api-query/guides/react-query/#acquire-and-mount-the-client). `useSuspenseQuery` provides defined data after its loading boundary resolves. An initial failure reaches the nearest React error boundary after retries finish. @@ -16,9 +16,9 @@ See [TanStack query cancellation](https://tanstack.com/query/latest/docs/framewo ## Define a displayable failure -This complete recipe uses a ready client for the following RPC contract. The application chooses -which declared messages are suitable for users. Error boundaries receive `unknown`; recognize the -execution wrapper and validate its Cause failure with Schema before reading a domain message. +This recipe uses a ready client for the RPC contract below. The application chooses which +declared messages users can see. Error boundaries receive `unknown`. Before reading a domain +message, recognize the execution wrapper and validate its Cause failure with Schema. Transport failures, defects, and unexpected errors use a generic fallback. ```tsx @@ -75,9 +75,9 @@ export const failureMessage = (error: unknown): string => { ## Reset both boundaries React owns the error boundary below. `QueryErrorResetBoundary` supplies its `onReset` callback. -The retry button first resets TanStack's failed-query boundary, then clears React's error state -so the component can mount and fetch again. This uses React's built-in Component class and -requires no additional error-boundary package. +The retry button resets TanStack's failed-query boundary, then clears React's error state. +The component can then mount and fetch again. The boundary uses React's built-in Component class +and requires no additional error-boundary package. ```tsx interface BoundaryProps { @@ -121,8 +121,8 @@ class RequestErrorBoundary extends Component { ## Preserve data after a failed refresh Use both boundaries around the component. This example disables automatic retries so the user -can choose when to retry. It retains native Suspense behavior: a failed initial load reaches the -boundary; a failed background refetch keeps cached data visible and displays an inline error. +can choose when to retry. Native Suspense behavior sends a failed initial load to the boundary. +A failed background refetch keeps cached data visible and displays an inline error. The Refresh button retries that background request. ```tsx @@ -156,18 +156,18 @@ export const UserScreen = ({ rpc }: { readonly rpc: Queries }) => ( ) ``` -Suspense throws query failures by default only when cached data is absent. If your application -must route failed refetches to the error boundary too, throw the observer error from the component -after fetching stops. Handle intentional `null` query data explicitly when using a void contract. +By default, Suspense throws query failures only when cached data is absent. If failed refetches +must also reach the error boundary, throw the observer error from the component after fetching stops. +When using a void contract, handle intentional `null` query data explicitly. See [TanStack Suspense recovery](https://tanstack.com/query/latest/docs/framework/react/guides/suspense#resetting-error-boundaries). ## Start independent reads together -Sequential `useSuspenseQuery` calls in one component can create a waterfall: the first hook -suspends before the later hook runs. Pass independent requests to `useSuspenseQueries` instead. -With generated options, explicit query descriptors retain the data and error types through -TanStack's parallel-hook overloads. Derive these descriptors from the leaf so changes to its -contract update every consumer. +Sequential `useSuspenseQuery` calls in one component can delay later reads. The first hook +suspends before the later hook runs. To start independent requests together, pass them to +`useSuspenseQueries`. Explicit query descriptors retain generated data and error types through +TanStack's parallel-hook overloads. Derive the descriptors from the leaf so contract changes +update every consumer. ```tsx export const TwoUsers = ({ rpc }: { readonly rpc: Queries }) => { @@ -190,5 +190,5 @@ export const TwoUsers = ({ rpc }: { readonly rpc: Queries }) => { ``` Render `TwoUsers` inside the same reset and Suspense boundaries. Both reads start together. -Choose a `staleTime` long enough that an earlier completed query remains fresh while the other -finishes; otherwise mounting after Suspense resolves can cause an immediate refetch. +Choose a `staleTime` that keeps the earlier completed query fresh until the other finishes. +Otherwise, mounting after Suspense resolves can cause an immediate refetch. diff --git a/apps/docs/src/content/docs/guides/svelte-query.md b/apps/docs/src/content/docs/guides/svelte-query.md index da59d24..f44f82c 100644 --- a/apps/docs/src/content/docs/guides/svelte-query.md +++ b/apps/docs/src/content/docs/guides/svelte-query.md @@ -3,8 +3,8 @@ title: Svelte Query description: Rebuild generated options from Svelte reactive input and keep client lifetime in the application. --- -Build generated options inside Svelte's reactive accessor so input changes produce a new key -and query function. This applies to both factories and every generated query view. +Build options inside Svelte's reactive accessor. When input changes, the accessor creates a new +key and query function. Use this pattern with either factory and every generated query view. The examples target Svelte 5.57.1 with Svelte Query 6.3.0 and Query Core 5.104.0. @@ -14,10 +14,10 @@ while their options can execute. ## Create native queries Create the utility tree in your application module from an acquired ready client. Provide a -QueryClient through Svelte Query's `QueryClientProvider`, then read reactive inputs inside the -accessor passed to `createQuery`. Keep the client's `Scope` and any serviceful runner alive while -queries use them. Here `users.read` accepts an ID and returns a user with `name`. -In the Svelte component script: +QueryClient through Svelte Query's `QueryClientProvider`. Read reactive inputs inside the accessor +passed to `createQuery`. Keep the client's `Scope` and any runner that supplies services alive +while queries use them. Here, `users.read` accepts an ID and returns a user with `name`. +Add the query to the Svelte component script: ```svelte ``` -Use `createInfiniteQuery(() => rpc.users.list.infiniteOptions(...))` for pagination and -`createMutation(() => rpc.users.write.mutationOptions(...))` for writes. A mutation's variables +For pagination, use `createInfiniteQuery(() => rpc.users.list.infiniteOptions(...))`. +For writes, use `createMutation(() => rpc.users.write.mutationOptions(...))`. A mutation's variables provide the input for each execution. Mutation options can be static when their callbacks and other settings do not depend on reactive state. @@ -44,16 +44,16 @@ Use the same accessor for `streamedOptions`, `liveOptions`, and HTTP `metadataOp ## Choose native options carefully A skipped query retains the defined-data type when it has defined `initialData`. An initial-data -function that may return `undefined` produces optional result data. +function that can return `undefined` produces optional result data. Compile the Svelte application with `module: "ESNext"` and `moduleResolution: "Bundler"`. -Svelte Query 6 requires this resolution for its emitted `.svelte` declarations; strict NodeNext +Svelte Query 6 requires this resolution for its emitted `.svelte` declarations. Strict NodeNext declaration checking reports an upstream `Box` import error. ## Own the native integration Follow the [shared integration lifetime](/effect-api-query/integrations/#own-the-cache-and-client-lifetime) -for providers, observer removal, stream finalizers, and accepted mutations. Match this native -wrapper to the application's Query Core and the framework's supported compiler/tooling. +for providers, observer removal, stream finalizers, and accepted mutations. Match the native +wrapper to the application's Query Core. Use a compiler and tooling that Svelte supports. -Server rendering and hydration remain application integrations. +The application owns server rendering and hydration. diff --git a/apps/docs/src/content/docs/guides/switch-cache-owners.md b/apps/docs/src/content/docs/guides/switch-cache-owners.md index cf0dda9..4dd2071 100644 --- a/apps/docs/src/content/docs/guides/switch-cache-owners.md +++ b/apps/docs/src/content/docs/guides/switch-cache-owners.md @@ -4,16 +4,15 @@ description: Isolate private queries, pending writes, and persisted directory da --- Include every safe identity that changes a result in the key prefix: tenant, user, session -generation, and permission generation. Use application identifiers and numeric generations; -bearer credentials never belong in keys or storage names. A permission change needs a new generation -even when the user and tenant stay the same. These values partition caches; the server still -owns authorization. +generation, and permission generation. Use application identifiers and numeric generations. +Keep bearer credentials out of keys and storage names. A permission change needs a new generation, +even when the user and tenant stay the same. These values partition caches. The server still owns authorization. ## Capture one owner In a consumer or mutation module, receive the ready `application` returned by [`startViteReactApplication(options)`](https://github.com/ueberBrot/effect-api-query/blob/main/examples/vite-react/src/lib/application.ts). -Capture that application and its existing utility trees and QueryClient: +Capture the application, its existing utility trees, and its QueryClient: ```ts const owner = application @@ -55,28 +54,30 @@ const createUser = owner.trackMutationOptions( ) ``` -The success callback captures its original owner. It checks that owner before seeding a concrete -query, and settlement invalidation uses the same guard. An old callback never looks up a current -application variable to choose where to write. Apply the guard to rollback, optimistic writes, -manual cache updates, and any later work after an `await` too. +The success callback captures its original owner. Before seeding a concrete query, it checks that +the owner is active. Settlement invalidation uses the same guard. An old callback never looks up +a current application variable to choose where to write. Guard rollback, optimistic writes, +manual cache updates, and any work after an `await` too. ## Replace the consumer boundary The [existing Vite application](https://github.com/ueberBrot/effect-api-query/blob/main/examples/vite-react/src/lib/application.ts) -provides `startViteReactApplication(options)`. Capture `previous`, unmount its consumer tree, -and await `previous.dispose()` before starting and rendering the replacement application. This -retires the old owner before constructing the new clients, QueryClient, and utilities. A failed acquisition leaves the -old owner inactive; handle that failure in the surrounding application. Use the same unmount, -retire, and replacement sequence when reconnecting clients with an unchanged semantic identity. +provides `startViteReactApplication(options)`. Capture `previous` and unmount its consumer tree. +Await `previous.dispose()` before starting and rendering the replacement application. The old +owner retires before the new clients, QueryClient, and utilities are constructed. If acquisition +fails, the old owner remains inactive. Handle the failure in the surrounding application. +When reconnecting clients with an unchanged semantic identity, use the same unmount, retirement, +and replacement sequence. [ViteReactExample](https://github.com/ueberBrot/effect-api-query/blob/main/examples/vite-react/src/app.tsx) keys its QueryClientProvider by a component key that changes with the application QueryClient. -Replacing that client therefore remounts query and mutation consumers, including when the safe -semantic identity stays the same. This component key is not part of query keys or persistence. Passing a new QueryClient through an existing -provider alone does not replace the QueryClient captured by existing hooks. Reassigning options -on a pending MutationObserver can also replace its callbacks. Capture originating ownership in -mutation context and replace observers at the handoff; arbitrary observer reassignment is outside -this recipe. +Replacing that client remounts query and mutation consumers, even when the safe semantic identity +stays the same. The component key is not part of query keys or persistence. Passing a new +QueryClient through an existing provider alone does not replace the QueryClient captured by existing hooks. + +Reassigning options on a pending MutationObserver can also replace its callbacks. Capture the +original owner in mutation context. Replace observers at the handoff. This recipe does not cover +arbitrary observer reassignment. Retirement immediately marks the captured owner inactive and removes its persisted snapshot. It cancels old queries and streams, clears both native caches, and waits for accepted mutation @@ -85,12 +86,13 @@ local stream cleanup before acquiring the next owner. Native `cancelQueries()` s is not a finalizer acknowledgement, and local disposal is not a remote server acknowledgement. The example attempts mutations immediately with `networkMode: 'always'` and `retry: false`. -It does not keep an offline mutation queue. `runMutation` tracks requests that have started; -new requests are rejected after retirement. Requests already accepted may complete on the server, -and switching owners does not undo them. Their `onSuccess` or `onSettled` callbacks may finish -after the request drain, so the ownership guard remains necessary. A hanging request or local -finalizer can delay the handoff; this policy supplies no completion deadline. Do not introduce -serialized mutation queues without an explicit ownership and shutdown policy. +It does not keep an offline mutation queue. `runMutation` tracks requests that have started. +New requests are rejected after retirement. Requests already accepted can complete on the server. +Switching owners does not undo them. Their `onSuccess` or `onSettled` callbacks can finish after +the request drain, so the ownership guard remains necessary. + +A hanging request or local finalizer can delay the handoff. This policy supplies no completion +deadline. Do not introduce serialized mutation queues without an explicit ownership and shutdown policy. ## Restore a directory snapshot @@ -102,20 +104,22 @@ the stored snapshot so an optimistic directory cannot be reused after reload. Storage names include the full safe owner tuple. Each envelope repeats that identity and includes `schemaVersion: 1` and `keyVersion: 1`. Restoration validates the complete envelope and all DTO -entries, checks both busters and all owner fields, then writes through keys derived by the current -utilities. Foreign or incompatible data is deleted before any cache write. A different owner's -storage slot is never read. Bump the schema buster when DTO meaning changes and the key buster -when the persisted view's key contract changes. +entries. It checks both busters and all owner fields, then writes through keys derived by the +current utilities. Before any cache write, it deletes foreign or incompatible data. It never reads +a different owner's storage slot. + +When DTO meaning changes, bump the schema buster. When the persisted view's key contract changes, +bump the key buster. Restored DTOs are plain data with `dataUpdatedAt: 0`, so ordinary queries refetch them. Class -constructors and methods are not restored by this recipe; use a paired codec from -[Hydrate Unary Data](/effect-api-query/guides/hydrate-unary-data/) when the domain requires them. +constructors and methods are not restored by this recipe. If the domain requires them, use a paired codec from +[Hydrate Unary Data](/effect-api-query/guides/hydrate-unary-data/). Default `dispose()` and owner switches erase persistence. The browser's pagehide cleanup explicitly uses `dispose({ discardPersistence: false })` to snapshot a settled directory for a reload while -retiring the old in-memory owner and releasing its clients. Old-owner persistence calls become -no-ops. Explicit disposal rejects on storage failure after query, cache, and client cleanup runs. +retiring the old owner in memory and releasing its clients. Persistence calls from the old owner +do nothing. Explicit disposal rejects on storage failure after query, cache, and client cleanup runs. The pagehide and HMR callbacks await that cleanup and report a rejection through the console. Accepted tracked commands finish before a replacement owner is acquired. Retiring their client -owner does not undo server work; use the explicit command cancellation operation when required. +owner does not undo server work. If cancellation is required, use the explicit command cancellation operation. diff --git a/apps/docs/src/content/docs/guides/tanstack-start.md b/apps/docs/src/content/docs/guides/tanstack-start.md index ac6b928..ca60e4b 100644 --- a/apps/docs/src/content/docs/guides/tanstack-start.md +++ b/apps/docs/src/content/docs/guides/tanstack-start.md @@ -4,20 +4,19 @@ description: Reuse generated options in Router loaders and components, then conn --- Use generated options with TanStack Router loaders and native Query hooks. TanStack Start adds -server rendering to the same Router/Query integration. Choose the factory for your Effect +server rendering to the same Router and Query integration. Choose the factory for your Effect contract and expose its utility tree beside the QueryClient in router context. Create `queryUtils` with the [RPC](/effect-api-query/getting-started/quick-start/) or [HTTP](/effect-api-query/getting-started/http-quick-start/) factory in your client module. Expose it and its QueryClient through router context. Use a safe identity prefix and a -signal-aware runner. The application owns the router, providers, API host and authentication. +runner that forwards the signal. The application owns the router, providers, API host, and authentication. ## Reuse options in a Router loader and component -Expose your `queryUtils` and `QueryClient` through the router context. For a -contract with a `users.list` operation, the same generated options work in both places: - -In the route module, register the loader and the existing `UsersRoute` component: +Expose `queryUtils` and `QueryClient` through router context. For a contract with `users.list`, +use the same generated options in the loader and component. In the route module, register the +loader and the existing `UsersRoute` component: ```tsx export const Route = createFileRoute('/users')({ @@ -39,21 +38,21 @@ the server and browser to address the same cache entry. For pagination, pass generated `infiniteOptions` to `queryClient.infiniteQuery` in the loader and `useInfiniteQuery` in the component. After a write, use generated `mutationOptions` and invalidate -the relevant group key. These are ordinary TanStack Query operations; see +the relevant group key. These use ordinary TanStack Query operations. See [Cache Keys and Filters](/effect-api-query/guides/cache-filters/) and [Generated Builders](/effect-api-query/reference/generated-builders/). ## Add Start server rendering -The loader/component pattern also works in a browser-only TanStack Router application. For Start -or another server-rendered host, connect native SSR query integration and give each request its -own resources and serialization contract. +The loader and component pattern also works in a browser-only TanStack Router application. +For Start or another server-rendered host, connect native SSR query integration. Give each request +its own resources and serialization contract. ### Own request lifetimes and hydration -Create a fresh Query Client and request-specific client, runner, and utility tree for each -server-rendered page. Keep authentication in that request's client or runner. A key prefix can -partition cached data by identity, but does not replace separate request ownership. +For each server-rendered page, create a fresh Query Client and a request-specific client, runner, +and utility tree. Keep authentication in that request's client or runner. A key prefix partitions +cached data by identity. Each request still needs its own resources. Connect the router to that Query Client with TanStack's SSR integration: @@ -71,11 +70,12 @@ setupRouterSsrQueryIntegration({ ``` The server dehydrates its Query Client. The browser hydrates its own Query Client and uses its -own ready client and runner. Register cleanup with the server request lifecycle. When SSR finishes -or the request aborts, cancel outstanding queries and await outstanding snapshot captures before -disposing their ready clients and runtime. Native cancellation alone may settle before local iterator -finalizers finish. Keep the browser -runtime alive for the browser application's lifetime. +own ready client and runner. Register cleanup with the server request lifecycle. +When SSR finishes or the request aborts, cancel outstanding queries. Await outstanding snapshot +captures before disposing their ready clients and runtime. Native cancellation alone can settle +before local iterator finalizers finish. + +Keep the browser runtime alive for the browser application's lifetime. Successful query data must satisfy your serializer's contract. If an endpoint returns decoded Schema class instances, decide whether the browser needs plain data or reconstructed instances. @@ -90,7 +90,7 @@ queries. The browser can then refetch an omitted query and receive a fresh `Effe or `EffectHttpApiQueryError`, including its Effect Cause, if the operation fails again. This avoids serializing an error and its Cause into the page. -When the page should render despite a loader failure, catch the loader rejection and use +If the page needs to render despite a loader failure, catch the loader rejection and use `useQuery` in the component to render pending and error states. Otherwise, allow the loader error to reach your route's error handling. See [Handle Failures](/effect-api-query/guides/handle-failures/) for inspecting typed failures. @@ -108,7 +108,7 @@ timeouts, aborts, stream policies, and exclusive query ownership. ## Explore the executable example -[Run the Start application](/effect-api-query/examples/) for both adapters. Copy the seam you need: +[Run the Start application](/effect-api-query/examples/) for both adapters. Copy the integration you need: | Task | Complete source | | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -117,7 +117,7 @@ timeouts, aborts, stream policies, and exclusive query ownership. | Request-owned clients and cleanup | [server-application.ts](https://github.com/ueberBrot/effect-api-query/blob/main/examples/tanstack-start/src/lib/server-application.ts) | | HTTP failure followed by browser refetch | [http-failure.tsx](https://github.com/ueberBrot/effect-api-query/blob/main/examples/tanstack-start/src/routes/http-failure.tsx) | -Server destinations must be trusted. The example uses request-owned server-local RPC execution; -its middleware captures server-owned authority that per-call headers or caller services cannot +Server destinations must be trusted. The example uses request-owned server-local RPC execution. +Its middleware captures server-owned authority that per-call headers or caller services cannot replace. Decoded-message RPC bypasses transport Schema codecs, so use a schema-aware protocol when wire validation or codec effects matter. Browser clients acquire separate network resources. diff --git a/apps/docs/src/content/docs/guides/trace-streams.md b/apps/docs/src/content/docs/guides/trace-streams.md index 647cb57..f38ecc8 100644 --- a/apps/docs/src/content/docs/guides/trace-streams.md +++ b/apps/docs/src/content/docs/guides/trace-streams.md @@ -3,16 +3,16 @@ title: Trace Stream Consumption description: Keep an application span open through HTTP SSE pulls and asynchronous iterator cleanup. --- -Configure telemetry in the application's runtime and instrument the stream that a ready client -returns. A span around the `runPromiseExit` creation Effect ends when it returns the iterable. -Actual stream pulls happen later; they need their own span. +Configure telemetry in your application runtime. Instrument the stream that the ready client +returns. A span around the `runPromiseExit` creation Effect ends when that Effect returns the +iterable. Stream pulls happen later and need their own span. ## Define the shared feed contract Place the string SSE feed declaration in `feed-contract.ts`. The server and client import the same contract for `GET /events`. -**Shared contract — `feed-contract.ts`** +Shared contract in `feed-contract.ts`: ```ts import { Schema } from 'effect' @@ -29,12 +29,12 @@ export const feedApi = HttpApi.make('feed-api').add( ## Attach a span to the decoded stream -In `trace-feed.ts`, the response transformation wraps only decoded streams and passes other values -through. HttpApiClient applies it before response-mode wrapping, so the client retains its native -request options and response contract. Keep header-wrapped stream schemas intact if your API -declares them; this simple feed has no decoded header wrapper. +In `trace-feed.ts`, the response transformation wraps only decoded streams. It passes other values +through. HttpApiClient applies the transformation before response-mode wrapping, so the client +retains its native request options and response contract. If your API declares header-wrapped +stream schemas, keep them intact. This feed has no decoded header wrapper. -**Client tracing — `trace-feed.ts`** +Client tracing in `trace-feed.ts`: ```ts import { QueryClient } from '@tanstack/query-core' @@ -65,22 +65,22 @@ const traceDecodedResponse = (effect: Effect.Effect) effect.pipe(Effect.map((value) => (Stream.isStream(value) ? traceFeed(value) : value))) ``` -`Stream.unwrap` acquires the scoped span when consumption begins. Providing its ParentSpan -makes nested pull work a child of that span. The finalizer updates the captured span directly, -so it does not depend on a pull fiber's current-span context. The span closes after local stream -finalizers finish, including asynchronous cancellation cleanup. +`Stream.unwrap` acquires the scoped span when consumption begins. Its ParentSpan makes nested +pull work a child of that span. The finalizer updates the captured span directly. It does not +depend on a pull fiber's current-span context. The span closes after local stream finalizers +finish, including asynchronous cancellation cleanup. The attributes identify the declared API and endpoint. The example records receipt and cleanup without exporting emitted data, request input, or credentials. ## Supply the runtime and await cleanup -Continue in `trace-feed.ts`. Use Effect's OTLP tracer with an exporter URL, such as your collector's -`/v1/traces` endpoint. This function configures a runtime, acquires the ready client, injects a -signal-aware runner, and captures one live value. Its snapshot helper stops the iterator and awaits -local cleanup before the runtime is disposed. +Continue in `trace-feed.ts`. Use Effect's OTLP tracer with an exporter URL, such as your +collector's `/v1/traces` endpoint. The function configures a runtime and acquires the ready client. +It injects a runner that forwards the signal and captures one live value. The snapshot helper +stops the iterator and awaits local cleanup before disposal releases the runtime. -**Client capture — `trace-feed.ts` (continued)** +Client capture in `trace-feed.ts`, continued: ```ts const captureTracedFeed = async (baseUrl: string, collectorUrl: string, signal: AbortSignal) => { @@ -118,13 +118,13 @@ const captureTracedFeed = async (baseUrl: string, collectorUrl: string, signal: Call `captureTracedFeed` with your API base URL, collector URL, and an application-owned AbortSignal. A browser collector needs to accept that browser's requests. The telemetry layer -and span policy belong to the application; the package does not install an exporter or log by default. +and span policy belong to the application. The package does not install an exporter or log by default. The same response transformation covers accumulated and live HTTP SSE views. -For RPC streams, instrument the stream in the application's client/transport integration while +For RPC streams, instrument the stream in the application's client and transport integration while preserving the flat client's request options and queue modes. Effect RPC also creates native client spans when tracing is enabled. Configure those spans and telemetry during client -acquisition; wrapping only iterable creation still does not cover later consumption. +acquisition. A wrapper around iterable creation alone does not cover later consumption. For an open browser feed, keep the runtime alive until the local stream finalizer signals completion. Native `cancelQueries` can settle before asynchronous cleanup finishes. Use diff --git a/apps/docs/src/content/docs/guides/vue-query.md b/apps/docs/src/content/docs/guides/vue-query.md index 12ca76a..94a12cf 100644 --- a/apps/docs/src/content/docs/guides/vue-query.md +++ b/apps/docs/src/content/docs/guides/vue-query.md @@ -3,16 +3,16 @@ title: Vue Query description: Rebuild generated options with Vue computed and keep client resources alive. --- -Pass generated options from either factory to Vue Query's native hooks. Configure `VueQueryPlugin` -and acquire the ready client before creating the utilities. +Pass options from either factory to Vue Query's native hooks. Before creating the utilities, +configure `VueQueryPlugin` and acquire the ready client. The examples target Vue 3.5.43 with Vue Query 5.104.0 and Query Core 5.104.0. ## Rebuild options when input changes -Builders capture plain input when called. Build options inside Vue `computed` so a changed input -produces a new key and query function. Passing a builder's result once leaves the query attached -to the original input. +Builders capture plain input when you call them. Build options inside Vue `computed`. +When input changes, the computed value contains a new key and query function. If you pass a +builder's result once, the query keeps using the original input. Keep [captured inputs unchanged](/effect-api-query/concepts/data-normalization/#keep-captured-inputs-unchanged) while their options can execute. @@ -48,15 +48,15 @@ const user = useQuery< ``` Read the selected value from `user.data.value`. The explicit native hook generics preserve the -leaf's data and error types while bounding Vue's recursive ref unwrapping. +leaf's data and error types while limiting Vue's recursive ref unwrapping. Automatic inference can otherwise exceed TypeScript's instantiation depth when it unwraps an -Effect `Cause` through a tagged key. Let the builder infer its arguments; put these type arguments +Effect `Cause` through a tagged key. Let the builder infer its arguments. Put these type arguments on the Vue hook. -Apply the same computed pattern to `infiniteOptions`, `streamedOptions`, `liveOptions`, and -`metadataOptions` when their input changes. Pass `rpc.users.write.mutationOptions()` to -`useMutation` and supply each execution's input through `mutate(variables)`. Use `computed` -when mutation settings depend on reactive state. +When input changes, use the same computed pattern for `infiniteOptions`, `streamedOptions`, +`liveOptions`, and `metadataOptions`. For writes, pass `rpc.users.write.mutationOptions()` to +`useMutation`. Supply each execution's input through `mutate(variables)`. If mutation settings +depend on reactive state, use `computed`. ## Choose native options carefully @@ -64,7 +64,7 @@ Vue evaluates a function-valued `enabled` as a getter with no arguments. Use a b computed options, or a zero-argument getter returning a boolean. A Core `enabled(query)` callback cannot be passed directly to Vue. -Vue accepts an initial-data initializer that may return `undefined`, with optional result data. +Vue accepts an initial-data initializer that can return `undefined`. Its result data is optional. An explicit `initialData: undefined` overrides inherited QueryClient initial data. ## Own the native integration diff --git a/apps/docs/src/content/docs/guides/websocket-clients.md b/apps/docs/src/content/docs/guides/websocket-clients.md index efb7ce4..b9ab6b2 100644 --- a/apps/docs/src/content/docs/guides/websocket-clients.md +++ b/apps/docs/src/content/docs/guides/websocket-clients.md @@ -3,7 +3,7 @@ title: WebSocket ready clients description: Acquire a shared RPC connection and keep cancellation and replacement application-owned. --- -Acquire a flat Effect RPC client inside an application Scope, then pass it to +Acquire a flat Effect RPC client inside an application Scope. Pass it to `createRpcQueryUtils`. The same connection can carry concurrent unary requests, accumulated streamed queries, and live queries. Each request retains its own query key and cancellation. @@ -12,7 +12,7 @@ and cancellation. Keep the flat ready client and its parent Scope alive. The [complete acquisition recipe](https://github.com/ueberBrot/effect-api-query/blob/main/tests/fixtures/socket-client.ts) -owns the socket, protocol, client and QueryClient cleanup. In that application setup: +owns cleanup for the socket, protocol, client, and QueryClient. In that application setup: ```ts const rpc = createRpcQueryUtils(socketGroup, { client, keyPrefix: ['socket', owner] }) @@ -20,7 +20,7 @@ const rpc = createRpcQueryUtils(socketGroup, { client, keyPrefix: ['socket', own The host supplies `Socket.layerWebSocketConstructorGlobal` in browsers or `NodeSocket.layerWebSocketConstructorWS` in Node. The server uses the same group and -serialization through `RpcServer.toHttpEffectWebsocket`; this recipe uses JSON. +serialization through `RpcServer.toHttpEffectWebsocket`. This recipe uses JSON. ## Read and capture stream data @@ -28,16 +28,16 @@ Use generated `queryOptions` for unary reads and native observers for `streamedO `liveOptions`. Independent keys share the connection without sharing cancellation. The first stream emission becomes successful data while `fetchStatus` remains `fetching`. -In a server/request loader, await `fetchStreamSnapshot(queryClient, options, controls)` +In a server request loader, await `fetchStreamSnapshot(queryClient, options, controls)` with the generated history or live options. For this recipe, `input` is -`{ channel: 'clock' }`; history uses +`{ channel: 'clock' }`. History uses `rpc.values.watch.streamedOptions({ input, maxChunks: 20 })`, while latest data uses -`rpc.values.watch.liveOptions({ input })`. Each capture has `timeoutMs: 5000` and settles -local cleanup before disposal. +`rpc.values.watch.liveOptions({ input })`. Each capture has `timeoutMs: 5000`. +Capture waits for local cleanup before disposal. The [complete typed snapshot recipe](https://github.com/ueberBrot/effect-api-query/blob/main/tests/fixtures/docs-websocket-use.ts) includes concurrent reads and disposal. [Stream Snapshots](/effect-api-query/guides/stream-snapshots/) -explains exclusive capture and local finalizer drain. `maxChunks` bounds cached history; +explains exclusive capture and local finalizer drain. `maxChunks` bounds cached history. `rpcOptions.streamBufferSize` bounds the transport queue. They govern different resources. ## Cancel and replace an owner @@ -48,11 +48,13 @@ concurrent unary calls continue on the same connection. TanStack retains its nat cancellation result: after a stream has published data, cancellation can settle its query promise successfully with cached data. -Drain local work and call `application.dispose` before disposing the server or an enclosing runtime. The -registered finalizer cancels queries and clears their cache before the client and socket -close. For a replacement owner, await disposal, acquire a new application Scope and -ready client, and construct a new QueryClient. The same semantic prefix can produce -the same keys while resource lifetimes and cache instances remain independent. Follow +Drain local work. Call `application.dispose` before disposing the server or an enclosing runtime. +The registered finalizer cancels queries and clears their cache before the client and socket +close. To replace an owner, await disposal. Acquire a new application Scope and ready client. +Construct a new QueryClient. + +The same semantic prefix can produce the same keys while resource lifetimes and cache instances +remain independent. Follow [owner-switching rules](/effect-api-query/guides/switch-cache-owners/) when authentication changes. Replacement acquires a new connection. Transport reconnection, backoff, credentials, @@ -64,18 +66,18 @@ from its last cached event. The WebSocket transport forwards an interrupted request to the RPC server. Cooperative handler finalizers were observed on this transport for both stream views. A local abort -signal or idle query state alone cannot prove remote completion; use a domain -acknowledgement when an application must wait for server work to stop. Closing a +signal or idle query state alone cannot prove remote completion. If the application must wait for +server work to stop, use a domain acknowledgement. Closing a connection immediately after cancellation can race delivery of that interrupt. An independent server interruption remains an `EffectRpcQueryError`, even when TanStack -has not cancelled the query. Its Cause preserves the failure received by the ready client; -wire serialization can change the original server's Cause representation. A failed stream +has not cancelled the query. Its Cause preserves the failure received by the ready client. +Wire serialization can change the original server's Cause representation. A failed stream leaves already published data available alongside its error. With Effect 4.0.0, cancelling a paused stream with a full transport buffer of size 1 can -stall other calls on the connection. This recipe uses the native default of 16. Verify -smaller buffers against the pinned transport version before changing that setting. +stall other calls on the connection. This recipe uses the native default of 16. Before changing +that setting, test smaller buffers against the pinned transport version. The default buffer is not a guarantee for larger chunks or unfinished queue offers. @@ -83,5 +85,5 @@ Keep streaming success schemas compatible on both ends. With Effect 4.0.0, a chu client Schema decoding can also fail unrelated calls sharing the connection. A larger buffer does not isolate decoding failures. -Query cancellation stops future work cooperatively. It does not undo completed writes; -use an [explicit cancellable command](/effect-api-query/guides/cancellation/) when the domain requires one. +Query cancellation cooperatively stops future work. It does not undo completed writes. +If the domain needs an explicit cancellation operation, use an [explicit cancellable command](/effect-api-query/guides/cancellation/). diff --git a/apps/docs/src/content/docs/index.mdx b/apps/docs/src/content/docs/index.mdx index c69fc7e..2339b75 100644 --- a/apps/docs/src/content/docs/index.mdx +++ b/apps/docs/src/content/docs/index.mdx @@ -19,29 +19,28 @@ hero: import { CardGrid, LinkCard } from '@astrojs/starlight/components' -`effect-api-query` combines an Effect declaration, a ready client, and your cache prefix into a -typed utility tree. Its leaves build options for native TanStack Query consumers: buffered reads, -mutations, pages, RPC streams, HTTP SSE, and buffered HTTP metadata. +`effect-api-query` builds a typed utility tree from an Effect declaration, a ready client, and your cache prefix. +Each leaf builds options that native TanStack Query consumers accept. +These options cover buffered reads, mutations, pages, RPC streams, HTTP SSE, and buffered HTTP metadata. ## Choose your contract -[Install the package](/effect-api-query/getting-started/installation/), then follow your adapter's -quick start for complete client acquisition, generated options, requests, and cleanup. Check -[Supported Operations](/effect-api-query/getting-started/feature-support/) for the views your -contract exposes. +[Install the package](/effect-api-query/getting-started/installation/), then follow the quick start for your adapter. +Each quick start shows how to acquire a client, build options, run requests, and release resources. +[Supported Operations](/effect-api-query/getting-started/feature-support/) lists the views that each contract exposes. ## Learn the library contract @@ -55,7 +54,7 @@ contract exposes. | Reuse contract types in application code | [Type Inference](/effect-api-query/reference/type-inference/) | | Inspect failures and release resources | [Handle Failures](/effect-api-query/guides/handle-failures/), [Client Lifecycle](/effect-api-query/concepts/client-lifecycle/) | -The library owns generated keys, functions, cache-view identity, and adaptation of Effect failures. +The library owns generated keys, functions, cache-view identity, and the conversion of Effect failures. TanStack owns the cache and its native consumers. Your application owns its ready client, transport, authentication, providers, runtime, and resource lifetime. @@ -63,18 +62,17 @@ transport, authentication, providers, runtime, and resource lifetime. Use the generated options with your existing native integration: -- [React Query](/effect-api-query/guides/react-query/) includes a complete client/provider setup and - links [Suspense recovery](/effect-api-query/guides/suspense-and-recovery/). +- [React Query](/effect-api-query/guides/react-query/) shows how to set up the client and provider. + Use [Suspense recovery](/effect-api-query/guides/suspense-and-recovery/) to handle Suspense failures. - [Vue](/effect-api-query/guides/vue-query/), [Solid](/effect-api-query/guides/solid-query/), [Svelte](/effect-api-query/guides/svelte-query/), and [Angular](/effect-api-query/guides/angular-query/) - explain each wrapper's reactive options and typing constraints. + explain how to rebuild reactive options and use each wrapper's types. - [Router and Start](/effect-api-query/guides/tanstack-start/) covers loaders, request ownership, and server rendering. [Hydration](/effect-api-query/guides/hydrate-unary-data/) and [Stream Snapshots](/effect-api-query/guides/stream-snapshots/) preserve decoded cache data. -The [integration overview](/effect-api-query/integrations/) explains the shared provider, -reactivity, and client-lifetime boundaries. [Run the examples](/effect-api-query/examples/) for -complete Vite React and TanStack Start applications. +The [integration overview](/effect-api-query/integrations/) explains how providers, reactivity, and client lifetime work together. +[Run the examples](/effect-api-query/examples/) to use complete Vite React and TanStack Start applications. Use [API Reference](/effect-api-query/reference/public-exports/) for exported values and types, and [Compatibility and Limits](/effect-api-query/reference/compatibility-and-limits/) for the diff --git a/apps/docs/src/content/docs/integrations/index.md b/apps/docs/src/content/docs/integrations/index.md index 55e375e..bf1e1d9 100644 --- a/apps/docs/src/content/docs/integrations/index.md +++ b/apps/docs/src/content/docs/integrations/index.md @@ -3,9 +3,9 @@ title: Integrations description: Connect generated Core options to native framework, router, server-rendering, and transport APIs. --- -`effect-api-query` produces static TanStack Query Core options. Use the native consumer that owns -your application: a framework hook, QueryClient, or router loader. The generated key, input, -query data, and Effect failure contract stay the same across these consumers. +`effect-api-query` builds static TanStack Query Core options. +Pass them to a native framework hook, QueryClient, or router loader in your application. +Each consumer uses the same generated key, input, query data, and Effect failure contract. ## Connect a native consumer @@ -19,43 +19,43 @@ query data, and Effect failure contract stay the same across these consumers. | [Angular](/effect-api-query/guides/angular-query/) | Signal accessors, injection context, and skipped-query result types | | [Router and Start](/effect-api-query/guides/tanstack-start/) | Shared loader/component options and independent SSR resources | -These pages explain how generated options participate in each native API. Use the framework's -own TanStack documentation for its provider, rendering, and reactive primitives. +Use the linked guides to connect generated options to each native API. +For providers, rendering, and reactive primitives, use the framework's own TanStack documentation. ## Rebuild options at the reactive boundary -Acquire the ready client and create its utility tree once for a cache owner. Call a builder inside -React render or the other framework's computed value/accessor when its input depends on reactive -state. Passing one previously built options object keeps the captured request attached to its -original input. +Acquire the ready client and create its utility tree once for a cache owner. +If input depends on reactive state, call the builder during React render or inside the other framework's computed value or accessor. +A previously built options object keeps the request tied to the input that it captured. Keep [captured inputs unchanged](/effect-api-query/concepts/data-normalization/#keep-captured-inputs-unchanged) while options can execute. The immutable key does not freeze arbitrary request values. -A new request needs new input and options. Mutation variables belong to each execution; -rebuild mutation options only when their callbacks or settings depend on reactive state. +A new request needs new input and options. Each mutation execution receives its own variables. +Rebuild mutation options only when their callbacks or settings depend on reactive state. ## Own the cache and client lifetime Provide the matching QueryClient through the native framework's provider or router context. -Match its Query wrapper to the application's Query Core version and use a compiler supported by -the framework tooling. The [compatibility reference](/effect-api-query/reference/compatibility-and-limits/) -records the tested combinations. - -A changed input or removed component detaches its observer. An abort-aware open stream cancels -when its last ordinary observer leaves; another observer can keep it active. TanStack's Suspense -hooks retain their documented cancellation limitation. Transport interruption depends on the -ready client. - -Stop consumers, cancel queries, await local finalizers and accepted mutations, then clear the cache -and release the client and runtime. Follow [Client Lifecycle](/effect-api-query/concepts/client-lifecycle/) +Use a Query wrapper that matches the application's Query Core version. +Use a compiler that the framework tooling supports. +The [compatibility reference](/effect-api-query/reference/compatibility-and-limits/) lists the tested combinations. + +A changed input or removed component detaches its observer. +An abort-aware open stream cancels when its last ordinary observer leaves. +Another observer can keep the stream active. TanStack's Suspense hooks retain their documented cancellation limitation. +Transport interruption depends on the ready client. + +Stop consumers and cancel queries. Wait for local finalizers and accepted mutations to finish. +Then clear the cache and release the client and runtime. +Follow [Client Lifecycle](/effect-api-query/concepts/client-lifecycle/) for disposal and [Switch Cache Owners](/effect-api-query/guides/switch-cache-owners/) when identity changes. Native cancellation can settle before asynchronous iterator cleanup finishes. ## Server rendering and hydration -A server request owns a fresh QueryClient and request-specific clients. The browser owns separate -resources, while equivalent inputs and prefixes address equivalent cache entries. Choose codecs -that preserve the decoded values across dehydration, hydration, and later client refetches. +Each server request owns a fresh QueryClient and request-specific clients. The browser owns separate resources. +Equivalent inputs and prefixes address equivalent cache entries on both sides. +Choose codecs that preserve decoded values during dehydration, hydration, and later client refetches. Use [Hydrate Query Data](/effect-api-query/guides/hydrate-unary-data/) for DTOs or Schema classes, [Hydrate Query Views](/effect-api-query/guides/hydrate-query-views/) for pages, metadata, and stream @@ -64,12 +64,11 @@ capture and local cleanup before disposing a request. ## Configure the client and host -Applications configure transport, authentication, middleware, runtime services, and telemetry -before passing a ready client to a factory. See +Before passing a ready client to a factory, configure transport, authentication, middleware, runtime services, and telemetry. See [WebSocket Clients](/effect-api-query/guides/websocket-clients/), [Browser and Worker Hosts](/effect-api-query/reference/browser-and-worker-hosts/), and [Stream Tracing](/effect-api-query/guides/trace-streams/). -[External HTTP Clients](/effect-api-query/reference/external-http-clients/) explains encoded OpenAPI -interop; those Promise clients do not replace the ready Effect HttpApiClient accepted by the factory. +[External HTTP Clients](/effect-api-query/reference/external-http-clients/) explains how to work with encoded OpenAPI clients. +Those Promise clients do not replace the ready Effect HttpApiClient that the factory accepts. For complete hosts and applications, [run the examples](/effect-api-query/examples/). diff --git a/apps/docs/src/content/docs/reference/browser-and-worker-hosts.md b/apps/docs/src/content/docs/reference/browser-and-worker-hosts.md index d039640..e3db0a5 100644 --- a/apps/docs/src/content/docs/reference/browser-and-worker-hosts.md +++ b/apps/docs/src/content/docs/reference/browser-and-worker-hosts.md @@ -3,32 +3,32 @@ title: Browser and Worker Hosts description: Ready-client ownership in browsers and dedicated workers, and the worker transport boundary. --- -Use the same factories and TanStack Query Core options in a browser window or dedicated worker. +The factories and TanStack Query Core options work in browser windows and dedicated workers. Each host acquires its own ready clients and owns their `Scope`, runner, QueryClient, and disposal. ## Own the worker lifetime -Create a utility tree inside the worker after acquiring its ready client. Run queries and maintain -their QueryClient in that worker. Messages to the window may carry application DTOs or events; -the utility tree, client, Effect runtime, and QueryClient remain owned by their host. +A worker creates its utility tree after it acquires a ready client. Queries and their QueryClient +run in that worker. Messages to the window can carry application DTOs or events. +The utility tree, client, Effect runtime, and QueryClient remain owned by their host. -On shutdown, stop accepting work, cancel the owner's queries, and await the application's active -stream finalizers and accepted mutations before clearing private cache data and closing the -client's `Scope`. Native query cancellation can settle before asynchronous finalizers finish. -Wait for application-owned cleanup acknowledgements before terminating the worker. Disposing one -owner must not close another owner's clients or cancel its queries. +Shutdown stops new work and cancels the owner's queries. The application awaits active stream +finalizers and accepted mutations before it clears private cache data and closes the client's +`Scope`. Native query cancellation can settle before asynchronous finalizers finish. +Worker termination requires acknowledgements that application-owned cleanup finished. +Disposing one owner must not close another owner's clients or cancel its queries. See [client lifecycle](/effect-api-query/concepts/client-lifecycle/) and [cancellation](/effect-api-query/guides/cancellation/) for the shared ownership contract. ## Worker transport boundary -A worker can own its ready client and QueryClient. Sending Effect RPC requests from a window to -a worker over `postMessage` requires a separate transport integration. +A worker can own its ready client and QueryClient. Effect RPC requests sent from a window to a +worker over `postMessage` require a separate transport integration. Effect exposes worker protocols through `effect/rpc` and worker services through `effect/workers`. -`RpcClient.layerProtocolWorker` requires application-supplied `WorkerPlatform` and `Spawner` -services. The server's worker protocol requires `WorkerRunnerPlatform`. The package supplies none -of these services and does not construct a worker transport. A browser worker transport needs a -compatible platform integration and its own acquisition, interruption, and disposal contract. -Supplying an already acquired client preserves the factories' existing ready-client boundary. +`RpcClient.layerProtocolWorker` requires `WorkerPlatform` and `Spawner` services supplied by the +application. The server's worker protocol requires `WorkerRunnerPlatform`. +The package supplies none of these services and does not construct a worker transport. +A browser worker transport needs a compatible platform integration with its own acquisition, +interruption, and disposal contract. An already acquired client preserves the factories' ready-client boundary. diff --git a/apps/docs/src/content/docs/reference/compatibility-and-limits.md b/apps/docs/src/content/docs/reference/compatibility-and-limits.md index 2f79274..96b24a4 100644 --- a/apps/docs/src/content/docs/reference/compatibility-and-limits.md +++ b/apps/docs/src/content/docs/reference/compatibility-and-limits.md @@ -6,7 +6,8 @@ description: Supported versions, RPC and HTTP operations, cache identity, and ru ## Supported integrations The package declares one exact Effect peer and Query Core `>=5.103.1 <6`. It is ESM-only and -targets ES2022. Use TypeScript with `strict: true`. The repository uses these versions: +targets ES2022. The published TypeScript contract requires `strict: true`. +The repository uses these versions: | Integration | Versions | | --------------------------------------------------- | ---------------------------------- | @@ -17,88 +18,87 @@ targets ES2022. Use TypeScript with `strict: true`. The repository uses these ve | TanStack Start | 1.168.60 | | TanStack Router and Router SSR Query | 1.170.41 and 1.167.3, respectively | -The stable Effect release contains unstable RPC and HTTP API modules. Keep the application's -Effect packages coordinated with the exact peer; see +The stable Effect release contains unstable RPC and HTTP API modules. +The application's Effect packages must stay coordinated with the exact peer. See [Compatibility and Stability](/effect-api-query/getting-started/compatibility-and-stability/). -Use native reactive accessors with generated Core options. See +Generated Core options work with native reactive accessors. See [Vue Query](/effect-api-query/guides/vue-query/), [Solid Query](/effect-api-query/guides/solid-query/), [Svelte Query](/effect-api-query/guides/svelte-query/), and [Angular Query](/effect-api-query/guides/angular-query/) -for option rebuilding, -compiler resolution, hook overloads, and resource ownership. +for option rebuilding, compiler resolution, hook overloads, and resource ownership. -Consult [package metadata](https://github.com/ueberBrot/effect-api-query/blob/main/package.json) for -peer ranges and the [workspace catalog](https://github.com/ueberBrot/effect-api-query/blob/main/pnpm-workspace.yaml) -for the pinned Effect release and example dependencies. +[Package metadata](https://github.com/ueberBrot/effect-api-query/blob/main/package.json) declares +peer ranges. The [workspace catalog](https://github.com/ueberBrot/effect-api-query/blob/main/pnpm-workspace.yaml) +pins the Effect release and example dependencies. ## Capability matrix -| Operation | RPC | HTTP | -| ---------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -| Ordinary queries, pagination, mutations | Unary RPCs | Buffered endpoints without multipart, regardless of method | -| Accumulated and live streams | Streaming RPCs | One unambiguous SSE success without multipart | -| Buffered multipart uploads | Client and payload contract owned by the application | Mutation-only; the application supplies `FormData` | -| Streaming multipart and raw byte streams | Client transport contract | Omitted endpoints | -| Response metadata | Declared RPC result | Buffered decoded data with plain status/header snapshots | -| Conditional reads | `skipToken` for input-bearing query builders | Same, including buffered metadata | -| Query cancellation | Signal forwarding and iterator cleanup; transport determines server interruption | Same; fetch can abort the pending request | -| Mutation cancellation | Explicit application command | Explicit application operation | -| Keys and failures | `rpc` namespace, dotted paths, full Cause | `http` namespace, literal paths, full Cause and declaration identity | +| Operation | RPC | HTTP | +| ---------------------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| Ordinary queries, pagination, mutations | Unary RPCs | Buffered endpoints without multipart, regardless of method | +| Accumulated and live streams | Streaming RPCs | One unambiguous SSE success without multipart | +| Buffered multipart uploads | Client and payload contract owned by the application | Mutation-only, with `FormData` supplied by the application | +| Streaming multipart and raw byte streams | Client transport contract | Omitted endpoints | +| Response metadata | Declared RPC result | Buffered decoded data with plain snapshots of status and headers | +| Conditional reads | `skipToken` for input-bearing query builders | Same, including buffered metadata | +| Query cancellation | Signal forwarding and iterator cleanup, with server interruption determined by transport | Same, with fetch able to abort the pending request | +| Mutation cancellation | Explicit application command | Explicit application operation | +| Keys and failures | `rpc` namespace, dotted paths, full Cause | `http` namespace, literal paths, full Cause and declaration identity | Clients, authentication, middleware, residual services, framework providers, host routes, SSR, serialization, mutation invalidation, and disposal belong to the application. The generated -Core options work with native framework wrappers; the framework guides describe their accessors +Core options work with native framework wrappers. The framework guides describe their accessors and inference limits. The package executes calls through the ready client. TanStack provides query cancellation -signals but has no corresponding mutation signal for the adapter to forward. Configure request -interception through client middleware and transport construction. Use an explicit domain -operation when server cancellation must be observable; compensation is a separate operation. +signals but has no corresponding mutation signal for the adapter to forward. +Client middleware and transport construction control request interception. +Observable server cancellation requires an explicit domain operation. Compensation is a separate operation. -For complete integrations, [run the examples](/effect-api-query/examples/). Streaming RPC -builders use TanStack's experimental `streamedQuery` helper. Use the ready client directly for -calls that do not need TanStack Query. +The [examples](/effect-api-query/examples/) provide complete integrations. +Streaming RPC builders use TanStack's experimental `streamedQuery` helper. +Calls that do not need TanStack Query can use the ready client directly. [Browser and worker hosts](/effect-api-query/reference/browser-and-worker-hosts/) explains ready-client ownership and the worker transport boundary. WebSocket clients support unary calls, both stream views, and application-owned connection -replacement. Follow the [WebSocket recipe](/effect-api-query/guides/websocket-clients/) for -buffering and chunk-decoding limits. Local cancellation alone does not prove remote completion. +replacement. The [WebSocket recipe](/effect-api-query/guides/websocket-clients/) describes buffering +and chunk-decoding limits. Local cancellation alone does not prove remote completion. -[External HTTP clients](/effect-api-query/reference/external-http-clients/) covers generated -OpenAPI consumption with `openapi-fetch` and `openapi-typescript`. Encoded DTOs, -multipart serialization, and raw SSE access retain their own contracts; the external client does -not replace a ready Effect client. +[External HTTP clients](/effect-api-query/reference/external-http-clients/) describes generated +OpenAPI consumption with `openapi-fetch` and `openapi-typescript`. Encoded DTOs, multipart +serialization, and raw SSE access retain their own contracts. The external client does not replace +a ready Effect client. With Effect 4.0.0, single-element arrays in GET and form-urlencoded payloads can fail server decoding. Array query parameters and JSON array responses retain their usual representation. ## Input and cache limits -Cache identity must be strict JSON. Encoding services, redacted values, and multiple HTTP payload -alternatives require safe [custom encoders](/effect-api-query/guides/custom-key-encoders/) -for endpoints with query support. Binary query input needs a JSON-safe projection. Multipart -mutation-only endpoints require no encoder and reject encoder entries. Encoders are synchronous; -asynchronous and Effect-returning encoders are unsupported. +Cache identity must be strict JSON. For endpoints with query support, encoding services, redacted +values, and multiple HTTP payload alternatives require safe +[custom encoders](/effect-api-query/guides/custom-key-encoders/). Binary query input needs a JSON-safe +projection. Multipart mutation-only endpoints require no encoder and reject encoder entries. +Encoders are synchronous. Asynchronous and Effect-returning encoders are unsupported. RPC payload Schemas must be query-stable because preparation and ready-client execution construct the payload separately. HTTP builders accept decoded request input without RPC construction. -Keep captured input unchanged while its options can run; frozen keys do not freeze arbitrary +Captured input must remain unchanged while its options can run. Frozen keys do not freeze arbitrary input or decoded data. Buffered queries and live emissions normalize successful `undefined` to `null`. Accumulated stream -elements and mutation results preserve it. See [Data Normalization](/effect-api-query/concepts/data-normalization/) -for payload construction, input ownership and query data, and -[Performance and Bundling](/effect-api-query/reference/performance/) for construction costs. +elements and mutation results preserve it. [Data Normalization](/effect-api-query/concepts/data-normalization/) +explains payload construction, input ownership, and query data. +[Performance and Bundling](/effect-api-query/reference/performance/) describes construction costs. ## Server rendering and errors -- The package does not serialize errors for SSR. Omit failed queries from dehydration and refetch - them in the browser, or provide your own error serializer. -- Await [fetchStreamSnapshot](/effect-api-query/guides/stream-snapshots/) to capture an open stream - before dehydration. A fresh capture exclusively owns an idle, unobserved exact query and awaits - local iterator cleanup. Its timeout limits the wait for data, not finalizer duration or remote - cancellation acknowledgement. +- The package does not serialize errors for SSR. Failed queries require an application error + serializer, or exclusion from dehydration followed by a browser refetch. +- [fetchStreamSnapshot](/effect-api-query/guides/stream-snapshots/) captures an open stream before + dehydration. A fresh capture exclusively owns an idle, unobserved exact query and settles after + local iterator cleanup. Its timeout limits the wait for data. It does not limit finalizer duration + or acknowledge remote cancellation. - HTTP binary data and other domain values need an application serialization strategy. - Both execution-error guards use `instanceof` and recognize errors from the same JavaScript realm. diff --git a/apps/docs/src/content/docs/reference/errors.md b/apps/docs/src/content/docs/reference/errors.md index 5441995..d625566 100644 --- a/apps/docs/src/content/docs/reference/errors.md +++ b/apps/docs/src/content/docs/reference/errors.md @@ -5,8 +5,8 @@ description: Configuration, key-generation, and execution errors for RPC and HTT ## `EffectRpcQueryConfigError` -Thrown synchronously for invalid factory or builder configuration. Stream execution also rejects -incompatible QueryClient hashing. Its `code` is one of: +Invalid factory or builder configuration throws this error synchronously. Stream execution also +rejects incompatible QueryClient hashing. The error's `code` is one of: - `InvalidMaxChunks`: a streamed-query bound is not a positive safe integer. - `InvalidRefetchMode`: a streamed-query mode is not `reset`, `append`, or `replace`. @@ -16,14 +16,14 @@ incompatible QueryClient hashing. Its `code` is one of: - `MissingKeyEncoder` - `UnknownKeyEncoder` - `UnsupportedQueryHash`: a builder received `queryKeyHashFn` or `queryHash`, or stream execution - found a default hash inconsistent with its key hasher or a conflicting cached identity. + found either a default hash inconsistent with its key hasher or a conflicting cached identity. -It can also expose `rpcTag`, `path`, and an underlying `cause`. +The error can also expose `rpcTag`, `path`, and an underlying `cause`. ## `EffectRpcQueryKeyError` -Thrown synchronously while preparing a payload-specific key. It exposes `rpcTag`, `cause`, and one -of these codes: +Payload-specific key preparation throws this error synchronously. The error exposes `rpcTag`, +`cause`, and one of these codes: - `PayloadConstructionFailed` - `PayloadEncodingFailed` @@ -32,24 +32,24 @@ of these codes: ## `EffectRpcQueryError` -Thrown when the RPC runner returns a failed `Exit`. It exposes `rpcTag`, `operation`, and the full -`Cause.Cause`. Use `isEffectRpcQueryError(value)` as the runtime guard within one JavaScript -realm; the guard uses `instanceof`. The operation is `query`, `infinite`, or `mutation`. +This error wraps a failed `Exit` from the RPC runner. It exposes `rpcTag`, `operation`, and the +full `Cause.Cause`. The runtime guard `isEffectRpcQueryError(value)` uses `instanceof` within +one JavaScript realm. The operation is `query`, `infinite`, or `mutation`. -Streaming failures use the same class and preserve RPC, stream, middleware, client, defect, and -interruption Causes. Their operation is `streamed` or `live`. +Streaming failures use the same class. They preserve RPC, stream, middleware, client, defect, and +interruption Causes, with operation `streamed` or `live`. -A rejected runner promise passes through unchanged because no Effect `Cause` exists to preserve. +A runner promise rejection passes through unchanged because it has no Effect `Cause` to preserve. ## `EffectRpcQueryEmptyStreamError` -Thrown when a live query's stream completes before emitting a value. It exposes the streaming RPC's -`rpcTag`. Accumulated streams return an empty array instead. +A live query whose stream completes before it emits a value throws this error. +The error exposes the streaming RPC's `rpcTag`. Accumulated streams return an empty array instead. ## `EffectHttpApiQueryConfigError` -Thrown synchronously for invalid factory or builder configuration. Stream execution also rejects -incompatible QueryClient hashing. Its `code` is one of: +Invalid factory or builder configuration throws this error synchronously. Stream execution also +rejects incompatible QueryClient hashing. The error's `code` is one of: - `InvalidMaxChunks`: a streamed-query bound is not a positive safe integer. - `InvalidRefetchMode`: a streamed-query mode is not `reset`, `append`, or `replace`. @@ -61,38 +61,39 @@ incompatible QueryClient hashing. Its `code` is one of: - `UnknownKeyEncoder` - `UnsupportedEndpointMetadata` - `UnsupportedQueryHash`: a builder received `queryKeyHashFn` or `queryHash`, or stream execution - found a default hash inconsistent with its key hasher or a conflicting cached identity. + found either a default hash inconsistent with its key hasher or a conflicting cached identity. -It exposes `apiId` and, when available, `groupId`, `endpoint`, `method`, `path`, and an underlying -`cause`. +The error exposes `apiId`. When available, it also exposes `groupId`, `endpoint`, `method`, `path`, +and an underlying `cause`. ## `EffectHttpApiQueryKeyError` -Thrown synchronously while preparing an HTTP query key, before client execution. It exposes -`apiId`, `groupId`, `endpoint`, `method`, `cause`, and one of these codes: +HTTP query key preparation throws this error synchronously, before client execution. +The error exposes `apiId`, `groupId`, `endpoint`, `method`, `cause`, and one of these codes: - `RequestEncodingFailed` - `KeyEncoderFailed` - `InvalidKeyValue` -See [HTTP cache identity and failures](/effect-api-query/reference/http-builders/#cache-identity-and-failures) -for each code's trigger. +[HTTP cache identity and failures](/effect-api-query/reference/http-builders/#cache-identity-and-failures) +describes each code's trigger. ## `EffectHttpApiQueryError` -Thrown when the HTTP runner returns a failed `Exit`. It exposes `apiId`, `groupId`, `endpoint`, +This error wraps a failed `Exit` from the HTTP runner. It exposes `apiId`, `groupId`, `endpoint`, `method`, `operation`, and the full `Cause.Cause`. The operation is `query`, `infinite`, `mutation`, `metadata`, `streamed`, or `live`. -Use `isEffectHttpApiQueryError(value)` to narrow errors within the same JavaScript realm. +The guard `isEffectHttpApiQueryError(value)` narrows errors within the same JavaScript realm. The Cause preserves endpoint, middleware, Schema, and HTTP client errors, including defects and interruption. SSE failures also preserve declared event errors, `Sse.Retry`, and `Sse.SseError`. -It can contain upstream requests, responses, or sensitive input values; inspect those -values before logging or exposing them. Runner rejections pass through unchanged. +The Cause can contain upstream requests, responses, or sensitive input values. +Those values need inspection before logging or exposure. Runner rejections pass through unchanged. ## `EffectHttpApiQueryEmptyStreamError` -Thrown when an HTTP live SSE query completes before emitting a value. It exposes `apiId`, -`groupId`, `endpoint`, `method`, and operation `live`. A decoded `undefined` emission counts as a -value and becomes `null`; a declared header wrapper preserves its body. Accumulated streams -complete with an empty or retained history according to their refetch mode. +An HTTP live SSE query that completes before it emits a value throws this error. +The error exposes `apiId`, `groupId`, `endpoint`, `method`, and operation `live`. +A decoded `undefined` emission counts as a value and becomes `null`. +A declared header wrapper preserves its body. Accumulated streams complete with an empty or +retained history according to their refetch mode. diff --git a/apps/docs/src/content/docs/reference/external-http-clients.md b/apps/docs/src/content/docs/reference/external-http-clients.md index 397e011..266569b 100644 --- a/apps/docs/src/content/docs/reference/external-http-clients.md +++ b/apps/docs/src/content/docs/reference/external-http-clients.md @@ -4,36 +4,36 @@ description: Generated OpenAPI consumers, encoded request values, multipart uplo --- An external HTTP client can call an Effect HttpApi server through its declared HTTP contract. -For example, use `openapi-typescript` to generate `paths` from the JSON document returned by -native `OpenApi.fromApi(api)` and call the server with `openapi-fetch`. +For example, `openapi-typescript` generates `paths` from the JSON document returned by native +`OpenApi.fromApi(api)`. The generated types describe calls made with `openapi-fetch`. The examples target `openapi-fetch` 0.17.0, `openapi-typescript` 7.13.0, and Effect 4.0.0. -`createHttpApiQueryUtils` accepts a ready Effect HttpApiClient. Keep that client and its runner -native; the external Promise client calls HTTP independently. This interoperability does not -establish compatibility with the Effect RPC wire protocol. +`createHttpApiQueryUtils` accepts a ready Effect HttpApiClient with a native Effect runner. +An external Promise client calls HTTP independently. Compatibility with the declared HTTP contract +does not establish compatibility with the Effect RPC wire protocol. ## Encoded and decoded values OpenAPI describes encoded HTTP values. An endpoint using `Schema.NumberFromString` accepts a string on the wire and a number through the ready Effect client. A generated external client -returns a plain encoded DTO; native Schema decoding constructs the domain value. -The external client's generated types add no runtime Schema decoding or validation. - -| Value | External client | Ready Effect client | -| ----------------------- | --------------------------------- | ------------------------------------------------------------- | -| Request path parameter | `params.path.id: '5'` | `params.id: 5` | -| Request query parameter | `params.query.limit: '2'` | `query.limit: 2` | -| Declared request header | `params.header['x-factor']: '3'` | `headers['x-factor']: 3` | -| JSON request payload | `body.amount: '7'` | `payload.amount: 7` | -| Response body | Plain object with `total: '17'` | Schema class with `total: 17` and methods | -| Response headers | Raw strings on `response.headers` | Decoded declared header wrapper; raw strings in metadata view | +returns a plain encoded DTO. Native Schema decoding constructs the domain value. +The external client's generated types provide no runtime Schema decoding or validation. + +| Value | External client | Ready Effect client | +| ----------------------- | --------------------------------- | ------------------------------------------------------------------ | +| Request path parameter | `params.path.id: '5'` | `params.id: 5` | +| Request query parameter | `params.query.limit: '2'` | `query.limit: 2` | +| Declared request header | `params.header['x-factor']: '3'` | `headers['x-factor']: 3` | +| JSON request payload | `body.amount: '7'` | `payload.amount: 7` | +| Response body | Plain object with `total: '17'` | Schema class with `total: 17` and methods | +| Response headers | Raw strings on `response.headers` | Decoded declared header wrapper, with raw strings in metadata view | ## Call the declared HTTP contract -Generate `paths` from your server's OpenAPI document. Its declared paths, headers and success/error -responses determine the external client interface; the utility tree's decoded input does not. +The server's OpenAPI document supplies `paths`. Declared paths, headers, success responses, and +error responses determine the external client interface. The utility tree's decoded input does not. -In the external client module, use the generated `paths` type and create the client: +The external client module uses the generated `paths` type: ```ts import createClient from 'openapi-fetch' @@ -43,7 +43,7 @@ import type { paths } from './openapi-generated.ts' const external = createClient({ baseUrl: 'https://api.example.test' }) ``` -For a contract declaring `POST /reports/:id` with these fields, send encoded values: +For a contract declaring `POST /reports/:id` with these fields, the request contains encoded values: ```ts const report = await external.POST('/reports/{id}', { @@ -60,10 +60,11 @@ if (report.error) { const revision = report.response.headers.get('x-revision') ``` -Read declared failures through `error` and inspect `response.status`; the external client does -not construct an Effect execution-error wrapper. Its `Response` keeps status and raw headers. -The native [metadata view](/effect-api-query/reference/http-builders/#buffered-metadata) instead -keeps decoded data and immutable plain status/header snapshots. +Declared failures appear in `error`, with the status in `response.status`. +The external client does not construct an Effect execution-error wrapper. +Its `Response` retains status and raw headers. The native +[metadata view](/effect-api-query/reference/http-builders/#buffered-metadata) retains decoded data +alongside immutable plain snapshots of status and headers. Array query parameters use repeated names, including a single-element array. JSON array responses retain their ordinary representation. GET and form-urlencoded payloads have separate @@ -71,24 +72,25 @@ responses retain their ordinary representation. GET and form-urlencoded payloads ## Serialize multipart uploads explicitly -Map a binary schema to `Blob` with the generator's `transform` hook. Supply a `bodySerializer` -that builds `FormData` from the declared text fields and files, preserving filenames. Let Fetch -set the multipart boundary; the default JSON serializer receives HTTP 415 from a multipart-only -endpoint. The ready Effect client instead takes `FormData` directly as its decoded payload; -see [Upload a File](/effect-api-query/guides/http-queries-and-mutations/#upload-a-file). +The generator's `transform` hook supports mapping a binary schema to `Blob`. +Multipart uploads require a `bodySerializer` that builds `FormData` from declared text fields and +files while preserving filenames. Fetch sets the multipart boundary. +The default JSON serializer receives HTTP 415 from a multipart-only endpoint. +The ready Effect client takes `FormData` directly as its decoded payload. +See [Upload a File](/effect-api-query/guides/http-queries-and-mutations/#upload-a-file). ## SSE boundary -Request `parseAs: 'stream'` for a raw `ReadableStream`. It does not parse events, decode -Schema values, or turn a reserved failure event into an error. Default JSON parsing fails on -an SSE response. +The `parseAs: 'stream'` option returns a raw `ReadableStream`. +It does not parse events, decode Schema values, or turn a reserved failure event into an error. +Default JSON parsing fails on an SSE response. -Effect's SSE data mode encodes each value as JSON in an event's `data` field. A custom event can -also carry `id` and `event`. A stream failure uses the reserved event name -`effect/http-api/stream/failure` and an encoded Cause body while HTTP status remains 200. -The generated document records these details in `x-effect-stream`; this client does not consume -that extension. Typed SSE consumption requires the native ready Effect client or an -application-owned compatible decoder. Raw stream access alone gives no typed SSE guarantee. +Effect's SSE data mode encodes each value as JSON in an event's `data` field. +A custom event can also carry `id` and `event`. A stream failure uses the reserved event name +`effect/http-api/stream/failure` and an encoded Cause body. HTTP status remains 200. +The generated document records these details in `x-effect-stream`, which this client does not consume. +Typed SSE consumption requires the native ready Effect client or a compatible decoder owned by the +application. Raw stream access alone gives no typed SSE guarantee. See the [external client's API](https://openapi-ts.dev/openapi-fetch/api) and [generator's Node API](https://openapi-ts.dev/node) for their configuration. diff --git a/apps/docs/src/content/docs/reference/factory.md b/apps/docs/src/content/docs/reference/factory.md index 3e87cae..979cc3b 100644 --- a/apps/docs/src/content/docs/reference/factory.md +++ b/apps/docs/src/content/docs/reference/factory.md @@ -5,16 +5,17 @@ description: Create an RPC utility tree from an RPC group and a ready client. ## `createRpcQueryUtils(group, options)` -Builds and freezes a `RpcQueryUtils` tree for the group's unary and streaming RPCs when called. +The factory builds and freezes a `RpcQueryUtils` tree for the group's unary and streaming RPCs +when called. ### Options | Option | Meaning | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `client` | Ready flat RPC client. The caller owns its `Scope` and lifecycle. | +| `client` | Ready flat RPC client whose `Scope` and lifecycle belong to the caller. | | `keyPrefix` | Non-empty, JSON-safe tuple that namespaces every generated key. | -| `runPromiseExit` | Runner used for RPC Effects. Required when client-side services remain; otherwise defaults to `Effect.runPromiseExit`. | -| `keyEncoders` | Synchronous encoders keyed by literal RPC tags. Required for payloads that need encoding services or contain redacted values; runtime inspection also requires them for opaque encoding middleware. | +| `runPromiseExit` | Runner for RPC Effects. It is required when client-side services remain. Otherwise, it defaults to `Effect.runPromiseExit`. | +| `keyEncoders` | Synchronous encoders keyed by literal RPC tags. Payloads that need encoding services or contain redacted values require them. Runtime inspection also requires them for opaque encoding middleware. | ```ts const rpcQuery = createRpcQueryUtils(rpcGroup, { @@ -24,9 +25,10 @@ const rpcQuery = createRpcQueryUtils(rpcGroup, { }) ``` -The factory throws `EffectRpcQueryConfigError` for an invalid prefix, RPC path collision, reserved -path segment, or invalid encoder map. It performs no network request. +An invalid prefix, RPC path collision, reserved path segment, or invalid encoder map causes +`EffectRpcQueryConfigError`. Factory construction performs no network request. -Query options retain the normalized payload for execution. Keep captured values unchanged; see +Query options retain the normalized payload for execution. Captured values must stay unchanged. +See [Data Normalization](/effect-api-query/concepts/data-normalization/#keep-captured-inputs-unchanged) for the distinction between immutable keys and caller-owned input. diff --git a/apps/docs/src/content/docs/reference/generated-builders.md b/apps/docs/src/content/docs/reference/generated-builders.md index 8f4ee8c..acd16a2 100644 --- a/apps/docs/src/content/docs/reference/generated-builders.md +++ b/apps/docs/src/content/docs/reference/generated-builders.md @@ -3,9 +3,9 @@ title: RPC Builders description: Branch and RPC leaf methods for keys, queries, mutations, and streams. --- -Every branch and leaf exposes `key()`, which returns its immutable cache-key prefix. +Every branch and leaf exposes `key()`. It returns the branch or leaf's immutable cache-key prefix. -Each unary RPC leaf also exposes: +Unary RPC leaves also expose these builders: | Builder | Result | | --------------------------- | ------------------------------------------------------------------------------------------------------------ | @@ -16,118 +16,119 @@ Each unary RPC leaf also exposes: | `mutationKey()` | Immutable operation key shared by mutations of this RPC. | | `mutationOptions(options?)` | Fresh mutation options with generated `mutationFn` and `mutationKey`. Pass variables when the mutation runs. | -Omitting `payload` or declaring `Schema.Void` makes an RPC payloadless. `Schema.Unknown`, -`Schema.Any`, and unions containing `Schema.Void` remain payload-bearing: key builders accept their -constructor input, query builders require an `input` field, and infinite queries require a page-input -mapper. Mutation variables and custom encoders retain the declaration's input and normalized payload -types, respectively. +An RPC is payloadless if it omits `payload` or declares `Schema.Void`. +Declarations using `Schema.Unknown`, `Schema.Any`, or unions containing `Schema.Void` remain +payload-bearing. Their key builders accept constructor input. Their query builders require an +`input` field, and infinite queries require a mapper from page parameters to input. +Mutation variables retain the declaration's input type. Custom encoders retain its normalized +payload type. Payloadless query builders take no input. Payload-bearing builders require an `input` field. -Disable one with `input: skipToken`, retaining its other TanStack options. See +The `input: skipToken` form disables a query and retains its other TanStack options. See [Queries](/effect-api-query/guides/queries/) and [Conditional Queries](/effect-api-query/guides/conditional-queries/) for usage. RPC payload Schemas with opaque encoding middleware require a custom key encoder, even when -the middleware is synchronous and service-free. TypeScript may not detect opaque middleware; -factory construction also validates this requirement at runtime. See +the middleware is synchronous and service-free. TypeScript cannot always detect opaque middleware. +Factory construction also validates this requirement at runtime. See [Custom Key Encoders](/effect-api-query/guides/custom-key-encoders/). -The builder consumes `input` and, for accumulated streams, `refetchMode` and `maxChunks`, -removing them from the returned options. -Skipped options retain the exact sentinel and operation-level key. +Builders remove `input` from the returned options. Accumulated-stream builders also remove +`refetchMode` and `maxChunks`. Skipped options retain the exact sentinel and operation-level key. `queryOptions`, `streamedOptions`, and `liveOptions` accept conditional `input` unions of payload constructor input and `skipToken`. Concrete inputs retain callable query functions and payload-specific keys. Conditional inputs retain the possible sentinel and both key shapes. -Inputs typed `unknown` or `any` can contain the sentinel and therefore return conditional options. +An input typed `unknown` or `any` can contain the sentinel, so it returns conditional options. -`skipToken` is valid only for payload-bearing query options. It is not accepted by key or mutation -builders, and skipped options are unsuitable for suspense and prefetch-only hooks. +Only payload-bearing query options accept `skipToken`. Key and mutation builders reject it. +Suspense and prefetch-only hooks cannot use skipped options. -Configure custom hashing through QueryClient global or prefix defaults. Builders reject per-call +QueryClient global or prefix defaults configure custom hashing. Builders reject per-call `queryKeyHashFn` and `queryHash` with `EffectRpcQueryConfigError` code `UnsupportedQueryHash`. -Keep hash defaults stable while entries remain cached. Stream execution also rejects with that -code when changed hashing conflicts with a matching cached entry. +Hash defaults must remain stable while entries are cached. If changed hashing conflicts with a +matching cached entry, stream execution also rejects with that code. ## Request-local RPC options -All option builders accept `rpcOptions`. The package forwards it to the ready RPC client on each -execution and removes it from the returned Query Core options, including skipped queries. +All option builders accept `rpcOptions`. The package forwards these options to the ready RPC +client on each execution. It removes them from the returned Query Core options, including skipped queries. | Builders | Request options | | ---------------------------------------------------- | ------------------------------------------------------------------- | | `queryOptions`, `infiniteOptions`, `mutationOptions` | `UnaryRpcOptions`: `headers` and `context` | | `streamedOptions`, `liveOptions` | `StreamingRpcOptions`: `headers`, `context`, and `streamBufferSize` | -`headers` accepts Effect's `Headers.Input`; `context` accepts `Context.Context`. -`streamBufferSize` is a number that configures the Effect client's stream buffer. It is independent of -`maxChunks`, which limits the accumulated query cache. +`headers` accepts Effect's `Headers.Input`. The `context` field accepts `Context.Context`. +`streamBufferSize` is a number that configures the Effect client's stream buffer. +It is independent of `maxChunks`, which limits the accumulated query cache. -Request options are fixed for each builder result. Infinite pages, retries, refetches, and repeated -mutations use those same options. Callbacks based on variables or page parameters are unsupported. -Omitting `rpcOptions` leaves the ready client's defaults in effect. +Each builder result fixes its request options. Infinite pages, retries, refetches, and repeated +mutations reuse those options. Callbacks based on variables or page parameters are unsupported. +If `rpcOptions` is omitted, the ready client's defaults remain in effect. -`discard` is unavailable because unary operations need their result. `asQueue` is unavailable -because the package adapts streams itself. Request options do not affect generated keys. If a -header changes the identity of the returned data, represent that identity in the RPC payload or -an application-owned key prefix to keep cache entries separate. +Unary operations need their result, so `discard` is unavailable. The package adapts streams itself, +so `asQueue` is unavailable. Request options do not affect generated keys. +If a header changes the identity of returned data, separate cache entries require that identity in +the RPC payload or an application-owned key prefix. See [Client Lifecycle](/effect-api-query/concepts/client-lifecycle/) for application-wide configuration. ## Build an infinite query -Map each page parameter to the unary RPC payload. The initial mapped payload also determines the -semantic cache key. See [Pagination](/effect-api-query/guides/pagination/) for usage. +The input mapper converts each page parameter to the unary RPC payload. +The initial mapped payload also determines the semantic cache key. +[Pagination](/effect-api-query/guides/pagination/) shows how to build a paginated query. -Payloadless RPCs omit `input`. To disable a payload-bearing infinite query, set its `input` field to -`skipToken`. The mapper and sentinel can also share one conditional input. +Payloadless RPCs omit `input`. A payload-bearing infinite query is disabled when its `input` +field is `skipToken`. The mapper and sentinel can also share one conditional input. -Concrete mappers retain callable query functions and payload-specific keys. The exact sentinel -retains the operation key; conditional inputs preserve both key shapes and the query-function -union. Mapper results must be payload constructor input. +Concrete mappers retain callable query functions and payload-specific keys. +The exact sentinel retains the operation key. Conditional inputs preserve both key shapes and the +query-function union. Mapper results must be payload constructor input. -A defined `initialData` value or factory remains required in the generated options, preserving -`useInfiniteQuery`'s defined-data inference. This applies to payload-bearing and payloadless RPCs, -including conditional and skipped inputs. A factory that may return `undefined` keeps hook data +When supplied, a defined `initialData` value or factory remains required in the generated options. +This preserves `useInfiniteQuery`'s defined-data inference for payload-bearing and payloadless RPCs, +including conditional and skipped inputs. A factory that can return `undefined` keeps hook data possibly undefined. -The builder forwards applicable Query Core options and owns `queryFn` and `queryKey`. +The builder owns `queryFn` and `queryKey` and forwards applicable Query Core options. QueryClient defaults own hashing. Each page uses the same Effect runner and cancellation signal as an ordinary query. ## Stream values -Each streaming RPC leaf exposes: +Streaming RPC leaves expose these builders: -| Builder | Result | -| ----------------------------- | ------------------------------------------------------------------------------ | -| `streamedKey(input, policy?)` | Data-tagged key for the accumulated sequence and its retention/refetch policy. | -| `streamedOptions(options?)` | Fresh Query Core options that append each emitted value to an array. | -| `liveKey(input?)` | Data-tagged key for the latest value. | -| `liveOptions(options?)` | Fresh Query Core options that replace the cached value after each emission. | +| Builder | Result | +| ----------------------------- | ---------------------------------------------------------------------------------- | +| `streamedKey(input, policy?)` | Data-tagged key for the accumulated sequence and its retention and refetch policy. | +| `streamedOptions(options?)` | Fresh Query Core options that append each emitted value to an array. | +| `liveKey(input?)` | Data-tagged key for the latest value. | +| `liveOptions(options?)` | Fresh Query Core options that replace the cached value after each emission. | See [Streaming](/effect-api-query/guides/streaming/) for usage. -Both option builders publish the query as successful after its first value and keep -`fetchStatus: 'fetching'` until the stream ends. An empty accumulated stream succeeds; a new -unseeded view resolves to `[]`. An empty live stream fails with `EffectRpcQueryEmptyStreamError`. +Both option builders publish the query as successful after its first value. +They keep `fetchStatus: 'fetching'` until the stream ends. An empty accumulated stream succeeds. +A new unseeded view resolves to `[]`. An empty live stream fails with `EffectRpcQueryEmptyStreamError`. -Live queries convert emitted `undefined` to `null`; explicit `null` stays `null`. Their keys, -selectors, and initial-data options describe this normalized value. Accumulated arrays preserve -`undefined` elements. +Live queries convert emitted `undefined` to `null`. Explicit `null` stays `null`. +Their keys, selectors, and initial-data options describe the normalized value. +Accumulated arrays preserve `undefined` elements. -For an inputless RPC, call `streamedKey(policy?)`. Omitted policy means unlimited history and -reset refetches. Pass the same policy to the key and options builders when -reading, seeding, cancelling, or refetching an exact accumulated view. +For an inputless RPC, the key builder takes the form `streamedKey(policy?)`. +Omitted policy means unlimited history and reset refetches. Exact reads, seeding, cancellation, +and refetches require the same policy in the key and options builders. -Accumulated streams accept TanStack's `refetchMode` option: +Accumulated streams accept these values for TanStack's `refetchMode` option: - `reset` clears cached values and returns the query to pending before refetching. This is the default. - `append` adds the new stream's values to the cached sequence. - `replace` retains the old sequence until the new stream ends, then replaces it atomically. -Set `maxChunks` to a positive safe integer to retain only the newest emitted elements in order. +A positive safe integer for `maxChunks` limits retention to the newest emitted elements in order. After each emission, the accumulated array contains at most `maxChunks` elements. On refetch: @@ -136,25 +137,24 @@ After each emission, the accumulated array contains at most `maxChunks` elements - `replace` builds a bounded replacement and publishes it when the stream completes. Initial fetches and append refetches trim existing data only when an element arrives. An empty -append refetch preserves existing data; empty reset and replace refetches finish with an empty -array. +append refetch preserves existing data. Empty reset and replace refetches finish with an empty array. -The bound discards older history to limit the number of elements; it does not limit byte size. -Without it, accumulation remains unbounded. Use `liveOptions` when only the latest value matters. +The bound discards older history and limits the number of elements. It does not limit byte size. +Without a bound, accumulation remains unbounded. The `liveOptions` builder retains only the latest value. -`maxChunks` and `refetchMode` both contribute to concrete accumulated-stream identity. Omitted -values mean unlimited retention and `reset`; explicit `undefined` and explicit `reset` produce -the same default identity. Different policies have separate histories, including concurrent views -of the same RPC payload. Root, branch, RPC, and streamed-operation prefixes still match every policy. +`maxChunks` and `refetchMode` both contribute to concrete accumulated-stream identity. +Omitted values mean unlimited retention and `reset`. Explicit `undefined` and explicit `reset` +produce the same default identity. Different policies have separate histories, including concurrent +views of the same RPC payload. Root, branch, RPC, and streamed-operation prefixes still match every policy. -Builders consume both fields before returning options, including skipped options. Invalid bounds -throw `EffectRpcQueryConfigError` with code `InvalidMaxChunks`; invalid modes use -`InvalidRefetchMode`. Both fail synchronously, even when `input` is `skipToken`. +Builders remove both fields before they return options, including skipped options. +Invalid bounds throw `EffectRpcQueryConfigError` with code `InvalidMaxChunks`. +Invalid modes use `InvalidRefetchMode`. Both fail synchronously, even when `input` is `skipToken`. -Derive exact accumulated-stream keys through the builders with the view's policy, and treat their -concrete suffix as opaque. Applications persisting data choose their own schema and key-version -busters; see [Switch Cache Owners](/effect-api-query/guides/switch-cache-owners/#restore-a-directory-snapshot). +Exact accumulated-stream keys come from the builders with the view's policy. Their concrete suffix +is opaque. Applications that persist data choose their own schema and key-version busters. +See [Switch Cache Owners](/effect-api-query/guides/switch-cache-owners/#restore-a-directory-snapshot). -Live queries always replace the cached value and therefore expose no `refetchMode`. Cancelling -or superseding a query closes its iterator and interrupts its Effect resources. Unmounting the -last observer cancels active work; other observers keep a shared query active. +Live queries always replace the cached value and expose no `refetchMode`. +When a query is cancelled or superseded, its iterator closes and its Effect resources are interrupted. +Unmounting the last observer cancels active work. Other observers keep a shared query active. diff --git a/apps/docs/src/content/docs/reference/http-builders.md b/apps/docs/src/content/docs/reference/http-builders.md index ffac846..db0c9f5 100644 --- a/apps/docs/src/content/docs/reference/http-builders.md +++ b/apps/docs/src/content/docs/reference/http-builders.md @@ -3,12 +3,12 @@ title: HTTP Builders description: HTTP query, metadata, mutation, pagination, and SSE option contracts. --- -Every retained branch and endpoint provides `key()` for prefix filtering. The -[HTTP Factory](/effect-api-query/reference/http-factory/) specifies construction, supported endpoint -shapes, and the decoded request contract. +Every retained branch and endpoint provides `key()` for prefix filters. +The [HTTP Factory](/effect-api-query/reference/http-factory/) defines construction, supported +endpoint shapes, and the decoded request contract. -Query key builders return immutable, data-tagged keys. Option builders return fresh native -Query Core options for the corresponding view. +Query key builders return immutable, data-tagged keys. +Option builders return fresh native Query Core options for each view. | Builder | View | | ----------------------------------------------------------- | ------------------------------------------------------------ | @@ -19,53 +19,52 @@ Query Core options for the corresponding view. | `streamedKey(input?, policy?)`, `streamedOptions(options?)` | Accumulated SSE emissions with retention and decoder policy. | | `liveKey(input?, policy?)`, `liveOptions(options?)` | Latest SSE emission with decoder policy. | -Input-bearing key builders require the complete request. Inputless streamed and live key -builders take their policy as the first argument. Buffered multipart endpoints expose only -mutation builders; SSE endpoints expose only accumulated and live builders. +Input-bearing key builders require the complete request. Inputless streamed and live key builders +take their policy as the first argument. Buffered multipart endpoints expose only mutation builders. +SSE endpoints expose only accumulated and live builders. ## Query options -See [Queries](/effect-api-query/guides/queries/) for usage. +[Queries](/effect-api-query/guides/queries/) shows how to use query options. -Ordinary options work with native query, suspense, and prefetch hooks and QueryClient operations. -Callbacks retain the decoded result, complete failure union, and request types. `select` changes -consumer data; data-tagged keys retain the underlying cache data and error types. The type of -`initialData`, whether defined or possibly undefined, preserves the corresponding TanStack hook -overload. +Ordinary options work with native query, suspense, and prefetch hooks, as well as QueryClient +operations. Callbacks retain the decoded result, complete failure union, and request types. +The `select` option changes consumer data. Data-tagged keys retain the underlying cache data and +error types. Defined or possibly undefined `initialData` preserves the corresponding TanStack hook overload. -Applicable TanStack options pass through. The package owns the key and function and removes -`input` before returning options. QueryClient global and prefix defaults own hashing. Builders +Applicable TanStack options pass through. The package owns the key and function. +It removes `input` before it returns options. QueryClient global and prefix defaults own hashing. Builders reject per-call `queryKeyHashFn` and `queryHash` with `EffectHttpApiQueryConfigError` code `UnsupportedQueryHash`. -Keep hash defaults stable while entries remain cached. Stream execution also rejects with that -code when changed hashing conflicts with a matching cached entry. +Hash defaults must remain stable while entries are cached. If changed hashing conflicts with a +matching cached entry, stream execution also rejects with that code. -Input-bearing `queryOptions` accepts a complete request in `input` or -`queryOptions({ input: skipToken, ...options })`. Import `skipToken` from `effect-api-query` or -TanStack. Skipping preserves caller options and returns the exact sentinel as `queryFn`, with the -operation-level key and no request identity. It performs no request encoding or client call. -Supplied initial data remains available, but native skipped hook data stays possibly undefined. +Input-bearing `queryOptions` accepts either a complete request in `input` or +`queryOptions({ input: skipToken, ...options })`. The `skipToken` binding is exported from +`effect-api-query` and TanStack. Skipping preserves caller options and returns the exact sentinel +as `queryFn`, with the operation-level key and no request identity. It performs no request encoding +or client call. Supplied initial data remains available, but native skipped hook data stays possibly undefined. -Inputless builders, key builders, and mutations reject `skipToken`. A skipped function cannot run -through manual refetch; supply valid input or use `enabled: false` with a complete request when -manual execution is required. Native suspense and prefetch-only hooks reject skipped options. +Inputless builders, key builders, and mutations reject `skipToken`. +A skipped function cannot run through manual refetch. Manual execution requires valid input or +`enabled: false` with a complete request. Native suspense and prefetch-only hooks reject skipped options. ## Mutation options -`mutationOptions(options?)` supplies `mutationKey` and `mutationFn`. Pass the complete decoded -request as variables when a native mutation hook or observer executes it. Variables never enter -the key. Callbacks retain result, request, and execution-error types. Successful `undefined` -remains `undefined`. +`mutationOptions(options?)` supplies `mutationKey` and `mutationFn`. +A native mutation hook or observer receives the complete decoded request as variables at execution. +Variables never enter the key. Callbacks retain result, request, and execution-error types. +Successful `undefined` remains `undefined`. -Mutations have no query abort signal and no automatic relationship to cached reads. Choose -invalidation, seeding, or optimistic updates in native callbacks; see -[Mutations](/effect-api-query/guides/mutations/). +Mutations have no query abort signal and no automatic relationship to cached reads. +Native callbacks control invalidation, seeding, or optimistic updates. +[Mutations](/effect-api-query/guides/mutations/) describes those choices. ## Buffered metadata Query-enabled buffered endpoints also provide `metadataKey(request)` and `metadataOptions({ input, -...options })`; inputless endpoints omit the request. The ready client runs in decoded-and-response -mode, and the cache contains: +...options })`. Inputless endpoints omit the request. The ready client runs in decoded-and-response +mode. The cache contains this envelope: ```ts type Metadata = { @@ -75,124 +74,136 @@ type Metadata = { } ``` -The outer envelope and copied plain header record are frozen. `data` preserves the declared decoded -value and its ownership, including decoded `WithHeaders` wrappers. Only top-level successful -`undefined` becomes `null`. Raw headers retain their string values without Effect's inspection -redaction; applications decide which headers may be persisted or dehydrated. +The adapter freezes the outer envelope and copied plain header record. +The `data` field preserves the declared decoded value and its ownership, including decoded +`WithHeaders` wrappers. Only top-level successful `undefined` becomes `null`. +Raw headers retain their string values without Effect's inspection redaction. +Applications decide which headers can be persisted or dehydrated. Fetched snapshots remain frozen after structural sharing. Global and prefix sharing policies apply -to fetched data; a policy passed to `metadataOptions` overrides them. Mutable envelopes or header -records selected by sharing are copied -before freezing, retaining the selected decoded data reference. The adapter does not freeze -decoded data or caller-supplied `initialData`, hydrated values, or manual cache writes. +to fetched data. A policy passed to `metadataOptions` overrides those policies. +If sharing selects mutable envelopes or header records, the adapter copies and freezes them while +retaining the selected decoded data reference. The adapter does not freeze decoded data, +caller-supplied `initialData`, hydrated values, or manual cache writes. -Selected observer results and, after execution, manual cache writes use the sharing policy explicitly -passed to `metadataOptions`, or standard deep sharing when it is absent. Inherited global and prefix -sharing policies apply only to fetched metadata snapshots. +Selected observer results use the sharing policy explicitly passed to `metadataOptions`, or +standard deep sharing if that policy is absent. After execution, manual cache writes use the same +rule. Inherited global and prefix sharing policies apply only to fetched metadata snapshots. A fresh-cache hit from imperative `queryClient.query(newOptions)` retains the Query's existing -options. Install a changed sharing policy through an observer or an actual fetch before relying -on it for later manual writes. +options. A changed sharing policy applies to later manual writes only after an observer or an +actual fetch installs it. -Metadata keys use a `metadata` discriminator and the ordinary request identity. Endpoint prefixes -match every view. Native `select`, `initialData`, skip-token inference, QueryClient hashing defaults, -and cancellation apply to metadata options. Execution failures identify operation `metadata` and -preserve that client mode's full error and service channels. Metadata adds no mutation or infinite -builders and is absent on multipart and streaming endpoints. +Metadata keys use a `metadata` discriminator and the ordinary request identity. +Endpoint prefixes match every view. Native `select`, `initialData`, skip-token inference, +QueryClient hashing defaults, and cancellation apply to metadata options. +Execution failures identify operation `metadata` and preserve that client mode's full error and +service channels. Metadata adds no mutation or infinite builders. +Multipart and streaming endpoints have no metadata view. -The cache retains the decoded value and metadata snapshot, without a response object or open body. +The cache retains the decoded value and metadata snapshot. It contains no response object or open body. See [Update with an ETag](/effect-api-query/guides/http-metadata/#update-with-an-etag) for conditional writes. ## Accumulated SSE options -`streamedOptions` accepts native query options, decoded `input` when declared, and: +`streamedOptions` accepts native query options, decoded `input` when declared, and these controls: -| Option | Contract | -| ------------------------- | ------------------------------------------------------------------------------------------------------- | -| `maxChunks` | Positive safe integer limiting retained events; omitted means unlimited. | -| `refetchMode` | `reset` (default), `append`, or `replace`. | -| `sseOptions.maxEventSize` | Positive safe integer limiting pending parser text; defaults to 10 MiB of JavaScript string code units. | +| Option | Contract | +| ------------------------- | ------------------------------------------------------------------------------------------------------------- | +| `maxChunks` | Positive safe integer that limits retained events. Omitted means unlimited. | +| `refetchMode` | `reset` (default), `append`, or `replace`. | +| `sseOptions.maxEventSize` | Positive safe integer that limits pending parser text. The default is 10 MiB of JavaScript string code units. | + +Decoded data is an ordered readonly array. Accumulated elements keep `undefined`. +Decoded header wrappers surround each emitted body. Empty completion succeeds. +Acquisition and consumption errors use `EffectHttpApiQueryError` with operation `streamed`. +They preserve declared event failures, `Sse.Retry`, `Sse.SseError`, HTTP and Schema errors, and +additional client channels. -Decoded data is an ordered readonly array. Accumulated elements keep `undefined`; decoded header -wrappers surround each emitted body. Empty completion succeeds. Acquisition and consumption errors -use `EffectHttpApiQueryError` with operation `streamed`, preserving declared event failures, -`Sse.Retry`, `Sse.SseError`, HTTP and Schema errors, and additional client channels. The caller's runner supplies acquisition and consumption services and receives Query's abort signal. Cancellation closes the iterator and follows native Query cache reversion. -`streamedKey(input, policy)` accepts the same decoder, retention, and refetch policy; inputless -endpoints use `streamedKey(policy)`. Equivalent defaults have equal keys. Different effective -policies occupy separate entries. Endpoint `key()` selects them all. Decoder controls are excluded -from decoded request input and custom encoders. Declared resume headers remain request identity. +`streamedKey(input, policy)` accepts the same decoder, retention, and refetch policy. +Inputless endpoints use `streamedKey(policy)`. Equivalent defaults have equal keys. +Different effective policies occupy separate entries. Endpoint `key()` selects them all. +Decoded request input and custom encoders exclude decoder controls. +Declared resume headers remain part of request identity. See [Accumulate Values](/effect-api-query/guides/streaming/#accumulate-values). ## Live SSE options `liveOptions` accepts native query options, declared decoded `input`, and `sseOptions`. -`liveKey(input, { sseOptions })`, or `liveKey({ sseOptions })` for inputless endpoints, includes -normalized decoder policy in its separate `live` identity. Its keys carry the latest decoded value -and the execution error union, including `EffectHttpApiQueryEmptyStreamError`. Live builders -preserve selection, initial-data, and skipped-input inference. +The key builder is `liveKey(input, { sseOptions })`, or `liveKey({ sseOptions })` for inputless +endpoints. It includes normalized decoder policy in the separate `live` identity. +Live keys carry the latest decoded value and the execution error union, including +`EffectHttpApiQueryEmptyStreamError`. Live builders preserve selection, initial-data, and +skipped-input inference. + +The first emission makes the query successful. Later emissions replace the cached value. +Completion preserves the latest value. Only a top-level `undefined` becomes `null`. +Declared header wrappers retain their decoded body and headers. -The first emission makes the query successful. Later emissions replace the cached value, and -completion preserves the latest value. Only a top-level `undefined` becomes `null`; declared -header wrappers retain their decoded body and headers. An empty live completion fails with -declaration metadata. Live queries accept no `maxChunks` or accumulated `refetchMode`. +An empty live completion fails with declaration metadata. +Live queries accept no `maxChunks` or accumulated `refetchMode`. -The acquisition and consumption error/service channels, decoder limit validation, iterator cleanup, -and caller-owned reconnection contract match accumulated SSE queries. Input-bearing builders -validate decoder policy even when skipped. Endpoint prefixes match both representations and every -concrete decoder policy. See [Keep the Latest Value](/effect-api-query/guides/streaming/#keep-the-latest-value). +Live and accumulated SSE queries have the same acquisition and consumption errors and services, +decoder limit validation, iterator cleanup, and caller-owned reconnection contract. +Input-bearing builders validate decoder policy even when skipped. Endpoint prefixes match both +representations and every concrete decoder policy. +See [Keep the Latest Value](/effect-api-query/guides/streaming/#keep-the-latest-value). ## Pagination -`infiniteOptions` requires `initialPageParam` and `getNextPageParam`. For an input-bearing endpoint, -`input(pageParam)` returns the complete decoded HTTP request for that page. Inputless endpoints -omit `input`. Input-bearing endpoints can pause with `input: skipToken` in the options object; -the direct sentinel form is unavailable. +`infiniteOptions` requires `initialPageParam` and `getNextPageParam`. +For an input-bearing endpoint, `input(pageParam)` returns the complete decoded HTTP request for +that page. Inputless endpoints omit `input`. Input-bearing endpoints can pause with +`input: skipToken` in the options object. The direct sentinel form is unavailable. -The initial request determines cache identity. Infinite keys use an `infinite` discriminator, so -they remain separate from ordinary queries for the same request. `infiniteKey(request)` builds -the corresponding key from a complete initial request; `infiniteKey()` serves inputless endpoints. -Keys returned by `infiniteOptions` also retain the inferred page-parameter type for cache reads. +The initial request determines cache identity. Infinite keys use an `infinite` discriminator to +stay separate from ordinary queries for the same request. +The `infiniteKey(request)` builder takes a complete initial request. +The `infiniteKey()` form serves inputless endpoints. Keys returned by `infiniteOptions` also retain +the inferred page-parameter type for cache reads. -Keep stable filters in the initial request and every page. Change the initial request when those -filters change, and advance cursors through `getNextPageParam`. The input mapper must be -deterministic: the builder evaluates it for the initial key, and execution evaluates it for every -page. +Stable filters belong in both the initial request and every page. Changes to those filters require +a changed initial request. The `getNextPageParam` callback advances cursors. +The input mapper must be deterministic because the builder evaluates it for the initial key and +execution evaluates it for every page. -QueryClient stores native `InfiniteData` and owns invalidation and refetching. Selections can -transform consumer data without changing cached pages. Every page preserves the -ordinary HTTP execution contract, including `undefined`-to-`null` normalization, wrapped errors, -and cancellation. See [Pagination](/effect-api-query/guides/pagination/). +QueryClient stores native `InfiniteData` and owns invalidation and refetching. +Selections can transform consumer data without changing cached pages. +Every page preserves the ordinary HTTP execution contract, including `undefined`-to-`null` +normalization, wrapped errors, and cancellation. See [Pagination](/effect-api-query/guides/pagination/). ## Cache identity and failures -HTTP keys begin with `keyPrefix`, `http`, and the HttpApi identifier, followed by the projected -endpoint path and operation discriminator. Query keys append canonical request identity when the -endpoint has input. `utils.key()` includes the generated root and matches every HTTP descendant. -RPC utilities use a separate `rpc` discriminator. Use the original caller prefix deliberately -when invalidating across both adapters. +HTTP keys begin with `keyPrefix`, `http`, and the HttpApi identifier. +The projected endpoint path and operation discriminator follow those root segments. +Query keys append canonical request identity when the endpoint has input. +The `utils.key()` prefix includes the generated root and matches every HTTP descendant. +RPC utilities use a separate `rpc` discriminator. Cross-adapter invalidation uses the original +caller prefix deliberately. Default query preparation synchronously encodes the declared `params`, `query`, `payload`, and -`headers` schemas. The key retains these labels, including when a bodyless method sends its payload -as URL parameters. Preparation uses the endpoint's effective schemas without constructing an HTTP -request or body. The same rule applies to JSON, text, form-urlencoded requests, and requests without -bodies. - -Encoded object members whose value is `undefined` are omitted. An encoded `null` stays `null`, -including when Effect's JSON codec produces it from a decoded optional value. Arrays retain their -order; sparse arrays and encoded `undefined` items are rejected. Header field names become lowercase; -equal duplicates collapse and conflicting duplicates fail. Other values follow strict canonical -JSON: finite numbers, plain objects, copied arrays, sorted object properties, cycle rejection, and -deep freezing. +`headers` schemas. The key retains these labels, even when a bodyless method sends its payload as +URL parameters. Preparation uses the endpoint's effective schemas without constructing an HTTP +request or body. JSON, text, form-urlencoded requests, and requests without bodies follow the same rule. + +Preparation omits encoded object members whose value is `undefined`. An encoded `null` stays +`null`, including when Effect's JSON codec produces it from a decoded optional value. +Arrays retain their order. Sparse arrays and encoded `undefined` items are rejected. +Header field names become lowercase. Equal duplicates collapse, and conflicting duplicates fail. + +Other values follow strict canonical JSON: finite numbers, plain objects, copied arrays, sorted +object properties, cycle rejection, and deep freezing. For endpoints with query support, multiple effective payload schemas require a custom encoder, -including alternatives with the same content type. Types enforce this requirement when declarations retain distinct schemas. Runtime -validation also covers alternatives that the declaration types no longer distinguish. Buffered binary -input needs an explicit JSON-safe projection because default keys cannot contain `Uint8Array`. See +including alternatives with the same content type. Types enforce this requirement when declarations +retain distinct schemas. Runtime validation also covers alternatives that the declaration types +no longer distinguish. Buffered binary input needs an explicit JSON-safe projection because default +keys cannot contain `Uint8Array`. See [custom key encoders](/effect-api-query/guides/custom-key-encoders/#http-requests). `EffectHttpApiQueryKeyError` identifies the API, group, endpoint, and method and distinguishes: @@ -203,17 +214,19 @@ input needs an explicit JSON-safe projection because default keys cannot contain | `KeyEncoderFailed` | A custom encoder throws. | | `InvalidKeyValue` | Encoded identity violates canonical JSON or has conflicting header names. | -These failures occur in `queryKey` or `queryOptions`, before the client runs. Mutation preparation -does not encode a query key; request encoding runs inside the ready client's Effect. Custom encoder -output follows strict JSON and bypasses the default HTTP rules for omitting `undefined` members -and normalizing headers. - -Multipart mutation keys contain no request variables or files. `mutationKey()` identifies the -operation, and `key()` retains the endpoint's normal invalidation prefix. Use mutation callbacks -to invalidate the affected read endpoints. Multipart leaves have no query or infinite-query builders. - -`EffectHttpApiQueryError` wraps a failed execution `Exit`, identifies the API, group, endpoint, -method, and operation, and preserves its complete Cause. The package adds no concrete request -values to that metadata; upstream Causes can still contain requests, responses, or Schema issue -values. `isEffectHttpApiQueryError` narrows execution errors. Configuration and key preparation -failures use `EffectHttpApiQueryConfigError` and `EffectHttpApiQueryKeyError` respectively. +These failures occur in `queryKey` or `queryOptions`, before the client runs. +Mutation preparation does not encode a query key. Request encoding runs inside the ready client's Effect. +Custom encoder output follows strict JSON. It bypasses the default HTTP rules that omit +`undefined` members and normalize headers. + +Multipart mutation keys contain no request variables or files. The `mutationKey()` builder +identifies the operation, and `key()` retains the endpoint's normal invalidation prefix. +Mutation callbacks can invalidate affected read endpoints. +Multipart leaves have no query or infinite-query builders. + +`EffectHttpApiQueryError` wraps a failed execution `Exit` and preserves its complete Cause. +It identifies the API, group, endpoint, method, and operation. +The package adds no concrete request values to that metadata. Upstream Causes can still contain +requests, responses, or Schema issue values. The `isEffectHttpApiQueryError` guard narrows execution errors. +Configuration failures use `EffectHttpApiQueryConfigError`. Key preparation failures use +`EffectHttpApiQueryKeyError`. diff --git a/apps/docs/src/content/docs/reference/http-factory.md b/apps/docs/src/content/docs/reference/http-factory.md index 7c855cb..df8e3c5 100644 --- a/apps/docs/src/content/docs/reference/http-factory.md +++ b/apps/docs/src/content/docs/reference/http-factory.md @@ -3,16 +3,16 @@ title: HTTP Factory description: HTTP factory options, decoded request input, response data, and cache keys. --- -`createHttpApiQueryUtils(api, options)` builds and freezes an HTTP utility tree when called. It -uses an Effect HttpApi and a ready HttpApiClient whose lifetime your application manages. Import -it from `effect-api-query`. +`createHttpApiQueryUtils(api, options)` builds and freezes an HTTP utility tree from an Effect +HttpApi and a ready HttpApiClient. The application manages the client's lifetime. +The factory is exported from `effect-api-query`. -Start with the [HTTP Quick Start](/effect-api-query/getting-started/http-quick-start/). -[HTTP Builders](/effect-api-query/reference/http-builders/) specifies each generated option and key. +The [HTTP Quick Start](/effect-api-query/getting-started/http-quick-start/) shows factory setup. +[HTTP Builders](/effect-api-query/reference/http-builders/) defines each generated option and key. -Ordinary groups project to `utils[groupIdentifier][endpointIdentifier]`; top-level groups -project endpoints to `utils[endpointIdentifier]`. Dots remain literal properties. Every retained -branch and endpoint has `key()`. +Ordinary groups project to `utils[groupIdentifier][endpointIdentifier]`. Top-level groups project +endpoints to `utils[endpointIdentifier]`. Dots remain literal properties. +Every retained branch and endpoint has `key()`. | Endpoint | Builders beside `key()` | | ------------------------------------------ | --------------------------------------------------------------------------------------- | @@ -20,8 +20,8 @@ branch and endpoint has `key()`. | Any buffered multipart payload alternative | `mutationKey`, `mutationOptions` | | Exactly one SSE success, no multipart | `streamedKey/Options`, `liveKey/Options` | -Any multipart alternative makes a buffered endpoint mutation-only, including mixed plain and -multipart alternatives. HTTP method does not determine the builder for query-enabled endpoints. +Any multipart alternative makes a buffered endpoint mutation-only, even if the endpoint also +accepts plain payloads. For query-enabled endpoints, the HTTP method does not determine the builder. ## Factory options @@ -32,54 +32,60 @@ multipart alternatives. HTTP method does not determine the builder for query-ena | `runPromiseExit` | Required when exposed endpoints or the ready client need execution services. Service-free calls default to Effect's runner. | | `keyEncoders` | Synchronous encoders keyed first by declaration group identifier, then endpoint identifier, including top-level groups. | -A key encoder receives the complete decoded HTTP request input and returns `JsonValue`. For endpoints -with query support, request encoding services, opaque encoding middleware, explicit redacted -values, and multiple payload alternatives require an encoder. Opaque middleware requires an -encoder even when it is synchronous and service-free; TypeScript may not detect that requirement, -so factory construction also validates it at runtime. -For alternatives, preserve every body and content-type distinction that affects the result. An -encoder does not provide execution services; the runner remains independently required. +A key encoder receives the complete decoded HTTP request input and returns `JsonValue`. +For endpoints with query support, an encoder is required for request encoding services, opaque +encoding middleware, explicit redacted values, or multiple payload alternatives. +Opaque middleware requires an encoder even when it is synchronous and service-free. +TypeScript cannot always detect that requirement, so factory construction also validates it at runtime. +For payload alternatives, the encoder must preserve every body and content-type distinction that +affects the result. + +An encoder does not provide execution services. The runner remains independently required. Multipart mutation-only endpoints require no encoder and reject configured encoder entries. ## Request and result contract HTTP input contains the endpoint's declared `params`, `query`, `headers`, and `payload` parts in -the ready client's request types. Params, query, headers, and ordinary payloads use decoded values; -multipart payloads use `FormData`. Mixed plain and buffered multipart alternatives preserve the -upstream client request union. Build multipart fields and files explicitly; the adapter forwards -your `FormData` to the ready client. It does not apply RPC constructor defaults. Inputless queries -need no input argument. Mutations receive the same complete request shape as their variables. - -Each declared container stays required even if all its fields are optional: a declared optional -query filter still needs `input: { query: {} }`. A `Schema.FiniteFromString` field accepts a number, -which the ready client encodes as a string. Raw response controls are excluded from query input, -mutation variables, and encoder input. - -Query options retain the decoded request for execution. Keep captured values unchanged; see +the ready client's request types. Params, query, headers, and ordinary payloads use decoded values. +Multipart payloads use `FormData`. Mixed plain and buffered multipart alternatives preserve the +upstream client request union. The caller explicitly builds multipart fields and files. +The adapter forwards the supplied `FormData` to the ready client. +HTTP builders do not apply RPC constructor defaults. + +Inputless queries need no input argument. Mutations receive the same complete request shape as +their variables. + +Each declared container stays required even if all its fields are optional. +A declared optional query filter still needs `input: { query: {} }`. +A `Schema.FiniteFromString` field accepts a number that the ready client encodes as a string. +Query input, mutation variables, and encoder input exclude raw response controls. + +Query options retain the decoded request for execution. Captured values must stay unchanged. +See [Data Normalization](/effect-api-query/concepts/data-normalization/#keep-captured-inputs-unchanged) for the distinction between immutable keys and caller-owned input. -Ordinary queries and mutations use decoded-only responses. Queries cache a successful `undefined` as `null`; -mutations retain `undefined`. Buffered text stays a string, binary data stays a `Uint8Array`, and -declared response-header wrappers retain their decoded body and headers. Applications own the -serialization strategy for binary and other domain values; the package supplies no automatic SSR -serializer. +Ordinary queries and mutations use decoded-only responses. Queries cache a successful `undefined` +as `null`. Mutations retain `undefined`. Buffered text stays a string, and binary data stays a +`Uint8Array`. Declared response-header wrappers retain their decoded body and headers. +Applications own serialization for binary and other domain values. The package supplies no +automatic SSR serializer. -Execution retains declared endpoint errors, middleware server/client errors, Schema errors, HTTP -client errors, and additional ready-client errors in the wrapped Cause's type. +The wrapped Cause's type retains declared endpoint errors, server and client middleware errors, +Schema errors, HTTP client errors, and additional ready-client errors. Required services include those needed by request encoders, success and error decoders, and the ready client for exposed endpoints. Compatible custom clients retain the errors and remaining -service requirements of their decoded-only and decoded-and-response call signatures; -response-only overloads contribute neither. +services from their decoded-only and decoded-and-response call signatures. +Response-only overloads contribute neither errors nor service requirements. See [client lifecycle](/effect-api-query/concepts/client-lifecycle/#http-clients-and-execution-services) and [cancellation](/effect-api-query/guides/cancellation/#cancel-an-http-query) for runtime ownership. -A single SSE success, including `WithHeaders(StreamSse(...), ...)`, supports accumulated and live queries. -Raw byte streams and mixed buffered/SSE success alternatives omit the complete endpoint. -Any streaming multipart request alternative does the same. SSE endpoints with buffered multipart -payload alternatives are also omitted. Buffered multipart alternatives retain -the endpoint's mutation builders. Groups containing only omitted endpoints disappear. +A single SSE success, including `WithHeaders(StreamSse(...), ...)`, supports accumulated and live +queries. The factory omits endpoints with raw byte streams or mixed buffered and SSE success +alternatives. It also omits any endpoint with a streaming multipart request alternative, and SSE +endpoints with buffered multipart payload alternatives. On buffered endpoints, multipart +alternatives retain mutation builders. Groups with only omitted endpoints disappear. + Factory construction rejects unsafe names, path collisions, and contradictory multipart metadata -before returning a tree. Preserve literal declaration types so the inferred tree omits the same -endpoints. +before it returns a tree. Literal declaration types let the inferred tree omit the same endpoints. diff --git a/apps/docs/src/content/docs/reference/performance.md b/apps/docs/src/content/docs/reference/performance.md index 5bd993c..8bf3090 100644 --- a/apps/docs/src/content/docs/reference/performance.md +++ b/apps/docs/src/content/docs/reference/performance.md @@ -1,26 +1,26 @@ --- title: Performance and Bundling -description: Construction costs, source workloads, and the distinction between adapter and application bundles. +description: Factory and key construction costs, measurement workloads, and adapter and application bundles. --- ## Construction -Factories eagerly build and freeze the complete RPC or HTTP utility tree. Build each tree once -for its ready client and cache owner. A larger contract increases factory work even when an -application uses only a few leaves. +Factories build and freeze the complete RPC or HTTP utility tree when called. +Each tree belongs to one ready client and cache owner. A larger contract increases factory work, +even when the application uses only a few leaves. Payload-bearing keys prepare a fresh immutable canonical payload. RPC preparation constructs -and Schema-encodes the normalized payload; HTTP preparation encodes the declared request parts. -Larger payloads increase this work. Executable query options also capture the request and create a fresh -query function. Repeated calls do not memoize payloads or option objects. +and Schema-encodes the normalized payload. HTTP preparation encodes the declared request parts. +Larger payloads require more preparation work. Executable query options also capture the request +and create a fresh query function. Repeated calls do not memoize payloads or option objects. A custom RPC key encoder replaces Schema encoding. Payload construction, JSON validation, -canonical copying, and freezing still run. Choose an encoder for correct semantic identity; -it must preserve every input field that can change the result. +canonical copying, and freezing still run. The encoder must preserve every input field that can +change the result so cache identity remains correct. ## Repeatable measurements -Run the optional construction workload against repository sources: +The optional construction workload measures repository sources: ```sh vp run construction-baseline @@ -28,8 +28,8 @@ vp run construction-baseline The [stream workload](https://github.com/ueberBrot/effect-api-query/blob/main/tests/create-rpc-query-utils-stream-baseline.test.ts) and [transport workload](https://github.com/ueberBrot/effect-api-query/blob/main/examples/server/tests/rpc-transport-overhead.test.ts) -measure publication and transport costs. Compare the same workload and dependency versions; -elapsed time and memory depend on the machine. +measure publication and transport costs. Results are comparable only for the same workload and +dependency versions. Elapsed time and memory depend on the machine. ## Adapter and application bundles @@ -37,6 +37,6 @@ The package marks its exports as free of side effects and keeps Effect and Query An application can import one factory or an error guard from the package root. Unused factories and the stream snapshot helper can then be removed by the bundler. -An adapter-only bundle with peers external excludes dependency code. An application's contract -Schemas, transport, QueryClient, framework, and other imports contribute to its final bundle. -Compare the same imports and dependency versions before attributing a size change to the adapter. +An adapter-only bundle with external peers excludes dependency code. The application's contract +Schemas, transport, QueryClient, framework, and other imports contribute to the final bundle. +Attributing a size change to the adapter requires a comparison with the same imports and dependency versions. diff --git a/apps/docs/src/content/docs/reference/public-exports.md b/apps/docs/src/content/docs/reference/public-exports.md index 959409f..3456193 100644 --- a/apps/docs/src/content/docs/reference/public-exports.md +++ b/apps/docs/src/content/docs/reference/public-exports.md @@ -3,92 +3,94 @@ title: Public Exports description: Package-root values and types. --- -All supported imports come from `effect-api-query`. - -| Export | Purpose | -| ----------------------------------------- | ------------------------------------------------------------------------- | -| `createRpcQueryUtils` | Build the RPC utility tree. | -| `viewPrefix` | Select every input of one supported leaf view. | -| `withQueryPolicy` | Bind typed defaults to an existing query options builder. | -| `withMutationPolicy` | Bind typed defaults to an existing mutation options builder. | -| `QueryPolicy`, `MutationPolicy` | Annotate reusable leaf-specific native policies. | -| `InferInput` | Derive the operation input from a generated leaf. | -| `InferQueryData`, `InferQueryError` | Derive cached data and failures for a supported query view. | -| `InferMutationData`, `InferMutationError` | Derive a buffered mutation's result and execution error. | -| `fetchStreamSnapshot` | Capture stream data and await exact-query cancellation and local cleanup. | -| `StreamSnapshotOptions` | Fresh/cached mode, request abort signal, and publication timeout. | -| `skipToken` | Query Core's exact sentinel for disabling queries that require input. | -| `EffectRpcQueryConfigError` | Invalid factory or builder configuration. | -| `EffectRpcQueryKeyError` | Payload or key preparation failure. | -| `EffectRpcQueryError` | Failed RPC `Exit` with its Effect `Cause`. | -| `EffectRpcQueryEmptyStreamError` | Live stream completed without a value. | -| `isEffectRpcQueryError` | Runtime guard for RPC execution errors. | -| `CreateRpcQueryUtilsOptions` | Factory option type. | -| `RpcQueryUtils` | Generated utility-tree type. | -| `RunPromiseExit` | Runner adapter type with optional abort signal. | -| `KeyEncoder` | Synchronous semantic key-encoder type. | -| `JsonValue` | Immutable strict-JSON key value. | -| `QueryData` | Query success type with possible `undefined` normalized to `null`. | -| `SkipToken` | Type of the exported skip sentinel. | -| `EffectRpcQueryConfigErrorCode` | Stable configuration error-code union. | -| `EffectRpcQueryKeyErrorCode` | Stable key error-code union. | - -The [HTTP factory](/effect-api-query/reference/http-factory/) adds `createHttpApiQueryUtils`, -`HttpApiQueryUtils`, `CreateHttpApiQueryUtilsOptions`, and `HttpApiKeyEncoder`. Its errors are +The package root, `effect-api-query`, exports all supported values and types. + +| Export | Purpose | +| ----------------------------------------- | ------------------------------------------------------------------------------ | +| `createRpcQueryUtils` | Builds the RPC utility tree. | +| `viewPrefix` | Selects every input of one supported leaf view. | +| `withQueryPolicy` | Binds typed defaults to an existing query options builder. | +| `withMutationPolicy` | Binds typed defaults to an existing mutation options builder. | +| `QueryPolicy`, `MutationPolicy` | Types for reusable native policies specific to a leaf. | +| `InferInput` | Derives operation input from a generated leaf. | +| `InferQueryData`, `InferQueryError` | Derive cached data and failures for a supported query view. | +| `InferMutationData`, `InferMutationError` | Derive a buffered mutation's result and execution error. | +| `fetchStreamSnapshot` | Captures stream data and waits for exact-query cancellation and local cleanup. | +| `StreamSnapshotOptions` | Fresh or cached mode, request abort signal, and publication timeout. | +| `skipToken` | Query Core's exact sentinel for disabling queries that require input. | +| `EffectRpcQueryConfigError` | Invalid factory or builder configuration. | +| `EffectRpcQueryKeyError` | Payload or key preparation failure. | +| `EffectRpcQueryError` | Failed RPC `Exit` with its Effect `Cause`. | +| `EffectRpcQueryEmptyStreamError` | Live stream completed without a value. | +| `isEffectRpcQueryError` | Runtime guard for RPC execution errors. | +| `CreateRpcQueryUtilsOptions` | Factory option type. | +| `RpcQueryUtils` | Generated utility-tree type. | +| `RunPromiseExit` | Runner adapter type with optional abort signal. | +| `KeyEncoder` | Synchronous semantic key-encoder type. | +| `JsonValue` | Immutable strict-JSON key value. | +| `QueryData` | Query success type with possible `undefined` normalized to `null`. | +| `SkipToken` | Type of the exported skip sentinel. | +| `EffectRpcQueryConfigErrorCode` | Stable configuration error-code union. | +| `EffectRpcQueryKeyErrorCode` | Stable key error-code union. | + +The [HTTP factory](/effect-api-query/reference/http-factory/) exports `createHttpApiQueryUtils`, +`HttpApiQueryUtils`, `CreateHttpApiQueryUtilsOptions`, and `HttpApiKeyEncoder`. Its error classes are `EffectHttpApiQueryError`, `EffectHttpApiQueryEmptyStreamError`, `EffectHttpApiQueryConfigError`, and `EffectHttpApiQueryKeyError`, with `isEffectHttpApiQueryError`, `EffectHttpApiQueryConfigErrorCode`, and `EffectHttpApiQueryKeyErrorCode` for narrowing and stable codes. -`UnaryRpcOptions` describes request-local headers and Context for unary builders. +`UnaryRpcOptions` describes headers and Context local to a request for unary builders. `StreamingRpcOptions` also accepts the Effect client's stream buffer size. -See [Generated Builders](/effect-api-query/reference/generated-builders/#request-local-rpc-options) for their behavior. +[Generated Builders](/effect-api-query/reference/generated-builders/#request-local-rpc-options) +defines their behavior. ## fetchStreamSnapshot -`fetchStreamSnapshot(queryClient, options, controls?)` returns `Promise`, preserving native -`select` and its result type. Supply a callable query function, usually through generated -`streamedOptions` or `liveOptions`. Skipped queries and infinite-query options are excluded. - -| Control | Default | Meaning | -| ----------- | --------- | ----------------------------------------------------------------------------------------------- | -| `mode` | `'fresh'` | Start an owned attempt; `'cached'` returns an existing successful value without acquiring work. | -| `signal` | Omitted | Reject on request abort using `signal.reason`. An already aborted signal acquires no work. | -| `timeoutMs` | `10_000` | Wait for publication or completion; a finite positive value at most `2_147_483_647`. | - -Fresh capture requires an idle exact query without observers and starts despite cached freshness -or future timestamps. `cached` preserves native selection, even when another owner is fetching -or observing that successful entry. If no successful entry exists, it uses fresh capture. -QueryClient owns hashing; helper options reject `queryHash` and `queryKeyHashFn`. - -| Outcome | Result | -| ---------------------------------------- | --------------------------------------------------------------------------------------- | -| First new publication | Return selected data after cancelling and finalizing the exact query. | -| Successful completion | Return the completed selected data. | -| Empty accumulated stream | Return the native empty history, subject to its refetch policy. | -| Empty live stream | Reject with the adapter's empty-stream error. | -| Terminal query failure | Reject with the original failure after native retry policy and local cleanup. | -| Timeout | Reject with a `DOMException` named `TimeoutError` after cancellation and local cleanup. | -| Request abort | Reject with the abort reason after cancellation and local cleanup. | -| External cancellation before publication | Reject with Query Core's `CancelledError` after local cleanup. | - -All outcomes detach listeners and the timer. Local cleanup must cooperate with cancellation and -finish before the Promise settles; the timeout does not bound finalizers or acknowledge remote -server cleanup. Keep the exact key exclusively owned throughout capture, including manual writes. -See [Stream Snapshots](/effect-api-query/guides/stream-snapshots/) for request disposal and independent -browser reconnection. +`fetchStreamSnapshot(queryClient, options, controls?)` returns `Promise` and preserves native +`select` and its result type. It requires a callable query function, usually from generated +`streamedOptions` or `liveOptions`. It excludes skipped queries and infinite-query options. + +| Control | Default | Meaning | +| ----------- | --------- | ------------------------------------------------------------------------------------------------- | +| `mode` | `'fresh'` | Starts an owned attempt. `'cached'` returns an existing successful value without acquiring work. | +| `signal` | Omitted | Rejects on request abort using `signal.reason`. An already aborted signal acquires no work. | +| `timeoutMs` | `10_000` | Limits the wait for publication or completion to a finite positive value at most `2_147_483_647`. | + +Fresh capture requires an idle exact query without observers. It starts even if cached data is +fresh or has a future timestamp. The `cached` mode preserves native selection, even when another +owner fetches or observes the successful entry. If no successful entry exists, it uses fresh capture. +QueryClient owns hashing. Helper options reject `queryHash` and `queryKeyHashFn`. + +| Outcome | Result | +| ---------------------------------------- | ---------------------------------------------------------------------------------------- | +| First new publication | Returns selected data after cancelling and finalizing the exact query. | +| Successful completion | Returns the completed selected data. | +| Empty accumulated stream | Returns the native empty history, subject to its refetch policy. | +| Empty live stream | Rejects with the adapter's empty-stream error. | +| Terminal query failure | Rejects with the original failure after native retry policy and local cleanup. | +| Timeout | Rejects with a `DOMException` named `TimeoutError` after cancellation and local cleanup. | +| Request abort | Rejects with the abort reason after cancellation and local cleanup. | +| External cancellation before publication | Rejects with Query Core's `CancelledError` after local cleanup. | + +Every outcome detaches listeners and the timer. Local cleanup must cooperate with cancellation +and finish before the Promise settles. The timeout does not bound finalizers or acknowledge +remote server cleanup. Capture requires exclusive ownership of the exact key, including manual writes. +[Stream Snapshots](/effect-api-query/guides/stream-snapshots/) describes request disposal and +independent browser reconnection. ## Type inference -Use [Type Inference](/effect-api-query/reference/type-inference/) for accepted inputs, supported +[Type Inference](/effect-api-query/reference/type-inference/) describes accepted inputs, supported query views, cached data before `select`, mutation results, and failure types. ## Select a view prefix `viewPrefix(leaf, view)` returns an immutable ordinary key for every input and stream policy of a -supported leaf view. Pass it to native query filters or `setQueryDefaults`. It carries no DataTag; -broad cache reads remain `unknown`. Use a concrete generated key for typed exact cache access. -See [Cache Filters](/effect-api-query/guides/cache-filters/) for encoded partial input. +supported leaf view. Native query filters and `setQueryDefaults` accept that key. +It carries no DataTag, so broad cache reads remain `unknown`. +Concrete generated keys provide typed exact cache access. +[Cache Filters](/effect-api-query/guides/cache-filters/) describes encoded partial input. ## Reuse typed policies @@ -96,5 +98,5 @@ See [Cache Filters](/effect-api-query/guides/cache-filters/) for encoded partial original options builder with shared policy fields applied first. Call-site fields replace matching defaults, including callbacks. `QueryPolicy` and `MutationPolicy` annotate reusable objects. Inputs, projections, initial data, pagination setup, and adapter controls remain local. -Shared mutation callbacks have an unknown optimistic result; supply `onMutate` and callbacks that -use its result at the call site. See [Defaults and Policies](/effect-api-query/guides/query-defaults/). +Shared mutation callbacks have an unknown optimistic result. The call site supplies `onMutate` +and callbacks that use its result. See [Defaults and Policies](/effect-api-query/guides/query-defaults/). diff --git a/apps/docs/src/content/docs/reference/type-inference.md b/apps/docs/src/content/docs/reference/type-inference.md index 7c1da5d..99da0bf 100644 --- a/apps/docs/src/content/docs/reference/type-inference.md +++ b/apps/docs/src/content/docs/reference/type-inference.md @@ -3,7 +3,7 @@ title: Type Inference description: Derive operation inputs, cached data, mutation results, and failures from generated leaves. --- -Import these types from `effect-api-query` and apply them to a generated leaf: +These types are exported from `effect-api-query`. Each describes a generated leaf's contract: | Type | Result | | ----------------------------- | --------------------------------------------------------- | @@ -13,12 +13,12 @@ Import these types from `effect-api-query` and apply them to a generated leaf: | `InferMutationData` | The successful buffered mutation result | | `InferMutationError` | The buffered mutation's execution error | -These helpers follow the utility tree's contract and ready-client types. Keep those declaration -names literal to preserve leaf paths and supported views. +The helpers follow the utility tree's contract and ready-client types. +Literal declaration names preserve leaf paths and supported views. ## Operation input -With the utility tree from your client module: +The utility tree from a client module supplies the leaf types: ```ts const input: InferInput = { id: 1 } @@ -27,16 +27,16 @@ type HistoryData = InferQueryData type UserError = InferQueryError ``` -RPC input is **payload constructor input**, so fields supplied by constructor defaults may be -omitted. HTTP input is the **decoded request** with its declared `params`, `query`, `headers`, and -`payload` containers. Multipart HTTP requests use `FormData` in the payload container. Inputless -leaves use `void`. `skipToken` disables an options-builder call; it does not change the leaf's -operation input contract. +RPC input is payload constructor input. Fields supplied by constructor defaults can be omitted. +HTTP input is the decoded request with its declared `params`, `query`, `headers`, and `payload` +containers. Multipart HTTP requests use `FormData` in the payload container. Inputless leaves +use `void`. A call to an options builder can use `skipToken` to disable execution without changing +the leaf's operation input contract. ## Query views and cached data -Query data and error helpers default to `query`. A streaming leaf needs an explicit `live` or -`streamed` view. Each leaf constrains `View` to its supported key and option builders: +Query data and error helpers default to `query`. Streaming leaves require an explicit `live` or +`streamed` view. A leaf constrains `View` to the views with supported key and option builders: | Leaf | Query views | | -------------------------------- | ------------------------------- | @@ -45,12 +45,12 @@ Query data and error helpers default to `query`. A streaming leaf needs an expli | RPC stream or supported HTTP SSE | `streamed`, `live` | | Buffered multipart HTTP | Mutation-only | -An unsupported explicit view fails typechecking. Omitting the view on a leaf with no ordinary -query produces `never`; specify its supported view. +An unsupported explicit view fails typechecking. If a leaf has no ordinary query, omitting the +view produces `never`. That leaf requires a supported explicit view. -Buffered and live query data normalize top-level successful `undefined` to `null`. Accumulated -histories preserve each emitted value. Infinite data includes pages and page parameters; metadata -contains decoded data, status, and headers. See +Buffered and live query data normalize top-level successful `undefined` to `null`. +Accumulated histories preserve each emitted value. Infinite data includes pages and page parameters. +Metadata contains decoded data, status, and headers. See [Data Normalization](/effect-api-query/concepts/data-normalization/) for ownership rules. `InferQueryData` describes cache contents. A native `select` changes the returned value: @@ -64,16 +64,17 @@ const selectedName = await queryClient.query(selectedOptions) const cachedUser = queryClient.getQueryData(selectedOptions.queryKey) ``` -Here `selectedName` is a string and `cachedUser` is the decoded user or `undefined`. The generated -key retains the unselected cache type. Broad prefix/filter reads can contain different views; -keep those results `unknown` until the application establishes their representation. +In this example, `selectedName` is a string, and `cachedUser` is the decoded user or `undefined`. +The generated key retains the unselected cache type. Broad prefix or filter reads can contain +different views. Their results remain `unknown` until the application establishes their representation. -Standalone infinite keys, and therefore `InferQueryData`, use `unknown` for page -parameters. An `infiniteOptions(...)` result retains the page parameter inferred from that call. +Standalone infinite keys use `unknown` for page parameters, as does +`InferQueryData`. An `infiniteOptions(...)` result retains the page parameter +inferred from that call. ## Mutation results -Infer the write contract separately: +The write contract has separate data and error types: ```ts type CreateUserInput = InferInput @@ -81,17 +82,17 @@ type CreatedUser = InferMutationData type CreateUserError = InferMutationError ``` -Mutation data preserves the successful decoded value, including `undefined`. Mutation callbacks -infer variables, data, errors, and their local `onMutate` result through the options builder. -Use [Defaults and Policies](/effect-api-query/guides/query-defaults/) to reuse typed callbacks while -preserving that local inference. +Mutation data preserves the successful decoded value, including `undefined`. +The options builder infers variables, data, errors, and the local `onMutate` result for mutation callbacks. +[Defaults and Policies](/effect-api-query/guides/query-defaults/) describes how reusable typed callbacks +preserve local inference. ## Failures at runtime Inferred errors describe declared execution and package failures. Execution errors retain the full -Effect `Cause`. A custom runner rejection or an error thrown by an application callback can pass -through unchanged; the inferred union does not validate those unknown failures. +Effect `Cause`. A custom runner rejection or an error from an application callback can pass through +unchanged. The inferred union does not validate those unknown failures. -Use the package's execution-error guards and refine domain failures before reading their fields. -See [Handle Failures](/effect-api-query/guides/handle-failures/) and the +The package's execution-error guards narrow execution errors. Domain failures need further narrowing +before their fields can be read. See [Handle Failures](/effect-api-query/guides/handle-failures/) and the [error reference](/effect-api-query/reference/errors/). diff --git a/docs/adr/0001-target-effect-4-release-candidate.md b/docs/adr/0001-target-effect-4-release-candidate.md index 98cef33..2047055 100644 --- a/docs/adr/0001-target-effect-4-release-candidate.md +++ b/docs/adr/0001-target-effect-4-release-candidate.md @@ -4,23 +4,21 @@ Status: Accepted. Extended by [ADR 0022](0022-add-buffered-http-utilities-with-s ## Context -Supporting Effect 3 and Effect 4 together in the first release would require two incompatible -RPC APIs. The repository targets stable Effect 4, whose RPC and HTTP API modules still declare -unstable APIs. +Supporting both Effect 3 and Effect 4 in the first release requires two incompatible RPC APIs. +The repository targets stable Effect 4. Its RPC and HTTP API modules still declare unstable APIs. ## Decision The first public release targets `effect/rpc` and `effect/http-api` from one exact Effect 4 release. -Each release declares and tests one exact Effect peer. Keep the exact pin while the adapters -depend on unstable upstream APIs. -Internal adapters isolate upstream extraction and invocation. +Each release declares and tests one exact Effect peer. While the adapters depend on unstable upstream +APIs, keep that exact pin. Internal adapters isolate upstream extraction and invocation. ## Consequences -Review upstream dependencies when adding an API or upgrading Effect. Each coordinated Effect -upgrade requires public-type, runtime, and native build checks. +When adding an API or upgrading Effect, review upstream dependencies. Each coordinated Effect +upgrade requires checks of public types, runtime behavior, and native builds. -Before `1.0`, breaking public changes raise the minor version and compatible changes raise the -patch. An Effect-only upgrade may raise the patch when the public API remains compatible. +Before `1.0`, breaking public changes raise the minor version. Compatible changes raise the patch. +If the public API remains compatible, an Effect-only upgrade can raise the patch. Before publishing `1.0`, evaluate the stability of the upstream RPC and HTTP APIs. -The Effect package's stable version alone does not establish the stability of those APIs. +A stable Effect package version does not establish that those APIs are stable. diff --git a/docs/adr/0002-let-call-sites-choose-query-or-mutation.md b/docs/adr/0002-let-call-sites-choose-query-or-mutation.md index 9698eb9..dfd8c72 100644 --- a/docs/adr/0002-let-call-sites-choose-query-or-mutation.md +++ b/docs/adr/0002-let-call-sites-choose-query-or-mutation.md @@ -4,15 +4,15 @@ Status: Amended by ADR 0018 and ADR 0020. ## Context -Effect RPC contracts do not classify operations as reads or writes. Requiring that classification -here would duplicate the contract. +Effect RPC contracts do not classify operations as reads or writes. Requiring a separate +classification here duplicates the contract. ## Decision -Every unary RPC leaf exposes `queryOptions` and `mutationOptions`. The call site chooses the TanStack -semantics. Leaves expose no direct execution helper. +Every unary RPC leaf exposes `queryOptions` and `mutationOptions`. Call sites choose the TanStack +semantics. Leaves have no direct execution helper. ## Consequences -ADR 0018 adds infinite queries to unary leaves. ADR 0020 supersedes this decision's omission of -streaming leaves. +ADR 0018 adds infinite queries to unary leaves. ADR 0020 adds streaming leaves and supersedes their +omission from this decision. diff --git a/docs/adr/0003-project-dotted-rpc-tags-into-utility-paths.md b/docs/adr/0003-project-dotted-rpc-tags-into-utility-paths.md index 7c707f5..528e3e0 100644 --- a/docs/adr/0003-project-dotted-rpc-tags-into-utility-paths.md +++ b/docs/adr/0003-project-dotted-rpc-tags-into-utility-paths.md @@ -4,22 +4,22 @@ Status: Amended by ADR 0018 and ADR 0020. Implementation ownership clarified by ## Context -Generated utilities need deterministic paths without a second name-mapping configuration. +Generated utilities need deterministic paths without a separate configuration that maps names. ## Decision -The literal Effect RPC tag is the sole source of its public utility path. Dot-separated segments -become nested properties. The factory rejects: +The literal Effect RPC tag determines its public utility path. Dot-separated segments become +nested properties. The factory rejects these segments and collisions: -- empty segments; -- reserved utility names; -- `__proto__`, `prototype`, and `constructor`; -- every collision between a leaf and a branch. +- Empty segments. +- Reserved utility names. +- `__proto__`, `prototype`, and `constructor`. +- Every collision between a leaf and a branch. -All other string segments are valid, including segments that require bracket notation. The factory -provides neither escaping nor a separate path map. +All other string segments are valid, including those that require bracket notation. The factory +provides no escaping or separate path map. ## Consequences -This trades a small set of tag restrictions for deterministic, string-free access. ADR 0018 and ADR -0020 extend the reserved utility names with their generated builders. +These tag restrictions provide deterministic access without strings. ADR 0018 and ADR 0020 add +their generated builder names to the reserved utility names. diff --git a/docs/adr/0004-build-core-shaped-options-and-certify-react-first.md b/docs/adr/0004-build-core-shaped-options-and-certify-react-first.md index 11b826c..82e7def 100644 --- a/docs/adr/0004-build-core-shaped-options-and-certify-react-first.md +++ b/docs/adr/0004-build-core-shaped-options-and-certify-react-first.md @@ -4,25 +4,25 @@ Status: Accepted. ## Context -Framework-specific adapters would widen the public surface before a framework requires a distinct -runtime contract. +Framework-specific adapters expand the public API before a framework requires a distinct runtime +contract. ## Decision -Generated options use static TanStack Query Core shapes. They pass unchanged to React Query hooks, -`QueryClient`-driven TanStack Router loaders, and TanStack Start. The package imports no framework -adapter and certifies React Query and TanStack Start first. +Generated options use static TanStack Query Core shapes. React Query hooks, +`QueryClient`-driven TanStack Router loaders, and TanStack Start accept them unchanged. +The package imports no framework adapter. It certifies React Query and TanStack Start first. -The library owns generated keys and functions. QueryClient global and prefix defaults own hashing -so generated fetches, stream publications, and native cache reads and writes share one entry. -Builders reject per-call hashing overrides because native key-only cache access cannot honor them. -Callers may provide every other applicable Query Core option. +The library owns generated keys and functions. QueryClient global and prefix defaults control +hashing. Generated fetches, stream publications, and native cache reads and writes therefore share +one entry. Builders reject per-call hashing overrides because native key-only cache access cannot +honor them. Callers can provide every other applicable Query Core option. Package-level policy helpers bind typed native defaults to a leaf's existing options builder. They preserve its overloads and apply call-site overrides after defaults. Inputs, selection, initial data, pagination mappers, adapter controls, and generated identity remain local to each -call. Mutation policies leave `onMutate` local; shared callbacks receive an unknown optimistic -result and are replaced by corresponding call-site callbacks. +call. Mutation policies leave `onMutate` local. Shared callbacks receive an unknown optimistic +result. Corresponding call-site callbacks replace them. ## Consequences diff --git a/docs/adr/0005-preserve-the-cause-in-one-error-wrapper.md b/docs/adr/0005-preserve-the-cause-in-one-error-wrapper.md index dfbfccf..31f12cc 100644 --- a/docs/adr/0005-preserve-the-cause-in-one-error-wrapper.md +++ b/docs/adr/0005-preserve-the-cause-in-one-error-wrapper.md @@ -4,21 +4,21 @@ Status: Amended by ADR 0018 and ADR 0020. Implementation ownership clarified by ## Context -Flattening Effect failures would discard typed failure, defect, interruption, and parallel-cause -information. +Flattening Effect failures discards information about typed failures, defects, interruptions, and +parallel Causes. ## Decision -Only a failed RPC `Exit` becomes an `EffectRpcQueryError`. It preserves the untouched Effect -`Cause`, the literal RPC tag, and the operation. ADR 0018 adds infinite-query operations; ADR -0020 adds accumulated-stream and live-query operations. +Only a failed RPC `Exit` becomes an `EffectRpcQueryError`. The error preserves the untouched +Effect `Cause`, literal RPC tag, and operation. ADR 0018 adds infinite-query operations. +ADR 0020 adds accumulated-stream and live-query operations. -Configuration and key-generation failures use `EffectRpcQueryConfigError` and +Configuration failures use `EffectRpcQueryConfigError`. Key-generation failures use `EffectRpcQueryKeyError`. User callbacks, TanStack callbacks, and runner rejections that produce no -`Exit` remain untouched. +`Exit` pass through unchanged. ## Consequences -Public errors expose discriminants and relevant tag or path metadata without retaining raw input or -encoder output. Query preparation fails synchronously. Failures inside the RPC Effect remain in its -Cause. +Public errors expose discriminants and relevant tag or path metadata. They retain neither raw +input nor encoder output. Query preparation fails synchronously. The Cause retains failures inside +the RPC Effect. diff --git a/docs/adr/0006-use-canonical-semantic-json-for-payload-keys.md b/docs/adr/0006-use-canonical-semantic-json-for-payload-keys.md index 305f4fd..7ca504e 100644 --- a/docs/adr/0006-use-canonical-semantic-json-for-payload-keys.md +++ b/docs/adr/0006-use-canonical-semantic-json-for-payload-keys.md @@ -4,18 +4,19 @@ Status: Accepted. Implementation ownership clarified by [ADR 0021](0021-share-ut ## Context -Raw constructor input and transport bytes do not provide stable semantic cache identity. +Neither raw constructor input nor transport bytes provide stable semantic cache identity. ## Decision -Query keys use a synchronous JSON representation of the normalized payload. The default path -constructs the payload, Schema-encodes it, and copies it into a deeply frozen canonical form with -sorted object keys and normalized numbers. A typed, tag-keyed encoder may replace Schema encoding. +Query keys use a synchronous JSON representation of the normalized payload. By default, the adapter +constructs the payload and Schema-encodes it. It then copies the encoded value into a deeply frozen +canonical form with sorted object keys and normalized numbers. A typed encoder indexed by tag can +replace Schema encoding. ## Consequences -Schemas that require encoding services or contain an explicit `Schema.Redacted`, including through -`Schema.suspend`, require a custom encoder. Callers remain responsible for other sensitive fields. -Values outside strict JSON, including sparse arrays, cycles, and non-plain objects, are rejected. -Prefixes and encoded payloads also reject objects with `__proto__` or `constructor` properties at -any depth. +If a Schema requires encoding services or contains an explicit `Schema.Redacted`, it requires a +custom encoder. This includes redacted values reached through `Schema.suspend`. Callers remain +responsible for other sensitive fields. The adapter rejects values outside strict JSON, including +sparse arrays, cycles, and non-plain objects. Prefixes and encoded payloads also reject objects with +`__proto__` or `constructor` properties at any depth. diff --git a/docs/adr/0007-keep-the-core-factory-lifecycle-neutral.md b/docs/adr/0007-keep-the-core-factory-lifecycle-neutral.md index b75c49d..e5fc653 100644 --- a/docs/adr/0007-keep-the-core-factory-lifecycle-neutral.md +++ b/docs/adr/0007-keep-the-core-factory-lifecycle-neutral.md @@ -4,19 +4,19 @@ Status: Accepted. Implementation ownership clarified by [ADR 0021](0021-share-ut ## Context -The application knows the required protocol, middleware, runtime, and resource lifetime in the -browser or on the server. +The application determines the protocol, middleware, runtime, and resource lifetime that it needs +in the browser or on the server. ## Decision The core factory accepts a ready flat Effect RPC client. It never constructs, scopes, or disposes -that client. Service-free calls default to `Effect.runPromiseExit`; residual Schema services require -an injected runner with the same `effect, { signal? }` shape. +that client. Service-free calls default to `Effect.runPromiseExit`. If Schema services remain, +inject a runner with the same `effect, { signal? }` shape. ## Consequences -The caller owns protocol, middleware, lifetime, and resources. Queries forward TanStack's abort -signal, mutations omit it, and runner rejections propagate untouched. +The caller owns the protocol, middleware, lifetime, and resources. Queries forward TanStack's +abort signal. Mutations omit that signal. Runner rejections propagate unchanged. [ADR 0026](0026-keep-rpc-transport-aggregation-in-the-application.md) preserves application ownership of transport aggregation. diff --git a/docs/adr/0008-use-flat-prefix-matchable-key-tuples.md b/docs/adr/0008-use-flat-prefix-matchable-key-tuples.md index 197d12b..b0c3926 100644 --- a/docs/adr/0008-use-flat-prefix-matchable-key-tuples.md +++ b/docs/adr/0008-use-flat-prefix-matchable-key-tuples.md @@ -4,19 +4,20 @@ Status: Amended by ADR 0018 and ADR 0020. Extended by [ADR 0022](0022-add-buffer ## Context -Applications need one key hierarchy for exact cache access and broad TanStack prefix filters. +Applications need a shared key hierarchy for exact cache access and broad TanStack prefix filters. ## Decision Generated keys concatenate a required readonly, non-empty JSON prefix, the adapter namespace, -the RPC path, an operation discriminator, and a canonical query payload when present. Concrete query keys use TanStack's -`DataTag`; prefix and mutation keys remain ordinary readonly keys. +the RPC path, and an operation discriminator. They append the canonical query payload when one +exists. Concrete query keys use TanStack's `DataTag`. Prefix and mutation keys remain ordinary +readonly keys. ## Consequences Root, branch, RPC, and operation keys support TanStack's native array-prefix matching. Mutation variables remain outside `mutationKey` because they arrive after option construction. The -package-level `viewPrefix(leaf, view)` helper selects a supported query view for every input and -stream policy of a leaf, preserving its literal prefix without a DataTag. Callers add native -TanStack filter conditions and encoded partial input themselves. ADR 0018 and ADR 0020 add operation discriminators without -changing the hierarchy. +package-level `viewPrefix(leaf, view)` helper selects a supported query view across every input and +stream policy of a leaf. It preserves the literal prefix without a DataTag. Callers add native +TanStack filter conditions and encoded partial input. ADR 0018 and ADR 0020 add operation +discriminators without changing the hierarchy. diff --git a/docs/adr/0009-build-an-eager-frozen-utility-tree.md b/docs/adr/0009-build-an-eager-frozen-utility-tree.md index e2451e2..ec96f24 100644 --- a/docs/adr/0009-build-an-eager-frozen-utility-tree.md +++ b/docs/adr/0009-build-an-eager-frozen-utility-tree.md @@ -4,16 +4,16 @@ Status: Accepted. Implementation ownership clarified by [ADR 0021](0021-share-ut ## Context -A Proxy or generated source would make the utility tree harder to inspect and introduce runtime or -build-time traps. +A Proxy or generated source makes the utility tree harder to inspect and introduces pitfalls at +runtime or during builds. ## Decision -The factory enumerates the RPC group's request map and eagerly builds an ordinary frozen object. It -validates the prefix, paths, collisions, and key-encoder configuration atomically before returning -the tree. +The factory enumerates the RPC group's request map and builds an ordinary frozen object eagerly. +Before returning the tree, it validates the prefix, paths, collisions, and key-encoder configuration +atomically. ## Consequences -Prefixes, keys, and canonical payloads are frozen. Generated option objects remain fresh and -unfrozen, and caller-owned option values remain untouched. +Prefixes, keys, and canonical payloads are frozen. Each generated option object is fresh and +unfrozen. Caller-owned option values remain untouched. diff --git a/docs/adr/0010-reuse-tanstack-query-core-as-a-peer.md b/docs/adr/0010-reuse-tanstack-query-core-as-a-peer.md index de1cb02..1d5b586 100644 --- a/docs/adr/0010-reuse-tanstack-query-core-as-a-peer.md +++ b/docs/adr/0010-reuse-tanstack-query-core-as-a-peer.md @@ -4,17 +4,17 @@ Status: Accepted. ## Context -Generated objects and declarations expose TanStack Query Core runtime values and types. +The generated objects and declarations expose runtime values and types from TanStack Query Core. ## Decision `@tanstack/query-core` is an external peer. Its supported v5 range must preserve `null` stream -data. The package uses Query Core's experimental streamed-query interface, so dependency updates -require public-type and runtime validation. The package re-exports only primitives required by its -own interface, including the exact `skipToken` and `SkipToken` bindings. +data. The package uses Query Core's experimental streamed-query interface. Dependency updates +therefore require validation of public types and runtime behavior. The package re-exports only +primitives that its interface requires, including the exact `skipToken` and `SkipToken` bindings. ## Consequences -Consumers use the same bindings, or broader Query APIs, from their TanStack installation. -`skipToken` applies only to payload-bearing query options. React, React Query, and TanStack Start -remain outside the peer set until a public entry point imports them directly. +Consumers can use the same bindings or broader Query APIs from their TanStack installation. +`skipToken` applies only to payload-bearing query options. Until a public entry point imports +React, React Query, or TanStack Start directly, those packages remain outside the peer set. diff --git a/docs/adr/0011-publish-esm-only.md b/docs/adr/0011-publish-esm-only.md index 249bc02..33a35fe 100644 --- a/docs/adr/0011-publish-esm-only.md +++ b/docs/adr/0011-publish-esm-only.md @@ -4,16 +4,16 @@ Status: Accepted. ## Context -Effect 4 is ESM-only. A CommonJS build would create a separate interop contract without making the -dependency graph CommonJS-native. +Effect 4 is ESM-only. A CommonJS build introduces a separate interoperability contract while the +dependency graph still requires ESM. ## Decision -The package publishes side-effect-free ES2022 ESM through one root export. Public barrels enumerate -named exports and avoid `export *`. Framework and internal subpaths become exports only when they are -intended for public use. +The package publishes side-effect-free ES2022 ESM through one root export. Public barrels list +named exports and avoid `export *`. Framework and internal subpaths become exports only when they +are intended for public use. ## Consequences -There is no CommonJS build. The package uses no Node-only APIs and declares no minimum Node -version. Applications may transform the complete dependency graph when their deployment requires it. +The package has no CommonJS build, uses no Node-only APIs, and declares no minimum Node version. +If deployment requires it, applications can transform the complete dependency graph. diff --git a/docs/adr/0012-normalize-undefined-query-success-to-null.md b/docs/adr/0012-normalize-undefined-query-success-to-null.md index b9d3788..fc56737 100644 --- a/docs/adr/0012-normalize-undefined-query-success-to-null.md +++ b/docs/adr/0012-normalize-undefined-query-success-to-null.md @@ -4,16 +4,16 @@ Status: Accepted. ## Context -TanStack Query rejects a successful `undefined` result. Effect RPC commonly uses `Schema.Void` and -runtime `undefined` for success. +TanStack Query rejects successful results whose value is `undefined`. Effect RPC commonly uses +`Schema.Void` and runtime `undefined` for success. ## Decision Buffered and live queries map successful `undefined` to cacheable `null` in both values and types. -Accumulated stream elements and mutation results preserve `undefined`, because neither needs this +Accumulated stream elements and mutation results preserve `undefined`. Neither needs this normalization to produce valid query data. ## Consequences -Every unary RPC and live emission remains usable as query data without disguising a failure or an -empty stream. This is the adapter's only success-value normalization. +Every unary RPC result and live emission remains usable as query data. The conversion does not +disguise failures or empty streams. The adapter makes no other success-value normalization. diff --git a/docs/adr/0013-use-a-vite-plus-workspace-with-a-root-package.md b/docs/adr/0013-use-a-vite-plus-workspace-with-a-root-package.md index 8ad37d5..91e8683 100644 --- a/docs/adr/0013-use-a-vite-plus-workspace-with-a-root-package.md +++ b/docs/adr/0013-use-a-vite-plus-workspace-with-a-root-package.md @@ -4,16 +4,15 @@ Status: Amended by ADR 0019 and [ADR 0022](0022-add-buffered-http-utilities-with ## Context -The repository needs one task graph without a speculative package split or a later move of the -initial release path. +The repository needs one task graph. The initial release path must stay in place without requiring +a package split before one is needed. ## Decision -Use a Vite+ pnpm workspace with the publishable `effect-api-query` package permanently at the root. -Keep examples private. +Use a Vite+ pnpm workspace. Keep the publishable `effect-api-query` package permanently at the root +and keep examples private. ## Consequences -Private examples share RPC contracts and hosting code. -ADR 0019 supersedes this decision's original requirement that both browser examples use the -standalone server. +Private examples share RPC contracts and hosting code. ADR 0019 replaces this decision's original +requirement that both browser examples use the standalone server. diff --git a/docs/adr/0014-require-query-stable-payload-construction.md b/docs/adr/0014-require-query-stable-payload-construction.md index 26b7e50..d3e4214 100644 --- a/docs/adr/0014-require-query-stable-payload-construction.md +++ b/docs/adr/0014-require-query-stable-payload-construction.md @@ -4,16 +4,16 @@ Status: Accepted. ## Context -Query options derive a synchronous key before execution. The ready RPC client then constructs the -retained normalized payload again. Effect does not guarantee that every Schema constructor accepts -its own `Type` or preserves encoded meaning across repeated construction. +Query options derive a key synchronously before execution. The ready RPC client reconstructs the +retained normalized payload when it executes. Effect does not guarantee that every Schema +constructor accepts its own `Type` or preserves encoded meaning when construction repeats. ## Decision -Query use requires a query-stable payload Schema. Standard struct-shaped payloads and materialized -defaults are the supported path. +Queries require a query-stable payload Schema. The supported path uses standard struct-shaped +payloads and materialized defaults. ## Consequences -Constructor-sensitive Schemas remain available to mutations. Query callers instead expose a stable -query-facing RPC; the initial release adds no custom execution seam for this case. +Mutations can still use constructor-sensitive Schemas. For queries, callers expose a stable +query-facing RPC. The initial release adds no custom execution seam for this case. diff --git a/docs/adr/0015-centralize-the-toolchain-in-vite-plus.md b/docs/adr/0015-centralize-the-toolchain-in-vite-plus.md index b2292e0..af7763a 100644 --- a/docs/adr/0015-centralize-the-toolchain-in-vite-plus.md +++ b/docs/adr/0015-centralize-the-toolchain-in-vite-plus.md @@ -4,17 +4,18 @@ Status: Accepted. ## Context -Parallel configuration stacks would let formatting, linting, type checking, tests, and packaging +Separate configuration stacks can let formatting, linting, type checking, tests, and packaging drift apart. ## Decision -Centralize those tasks in Vite+'s `vite.config.ts` and integrated tools. Keep separate tools only for -capabilities Vite+ does not own, such as Astro diagnostics, Effect-aware test helpers, full-process -browser tests, public compiler fixtures, and release management. +Centralize these tasks in Vite+'s `vite.config.ts` and integrated tools. Keep separate tools only +for capabilities that Vite+ does not own. These include Astro diagnostics, Effect-aware test +helpers, full-process browser tests, public compiler fixtures, and release management. ## Consequences -Library development uses TypeScript 7 with matching Effect diagnostics. Public declarations are -also checked with TypeScript 5.9. The documentation package may use a compiler supported by Astro. -Native package validation remains part of the build; dependency-update automation remains deferred. +Library development uses TypeScript 7 with matching Effect diagnostics. Checks of public +declarations also use TypeScript 5.9. The documentation package can use a compiler that Astro +supports. Native package validation remains part of the build. Dependency-update automation remains +deferred. diff --git a/docs/adr/0016-automate-releases-with-changesets.md b/docs/adr/0016-automate-releases-with-changesets.md index 816c8c7..c938eae 100644 --- a/docs/adr/0016-automate-releases-with-changesets.md +++ b/docs/adr/0016-automate-releases-with-changesets.md @@ -4,19 +4,19 @@ Status: Accepted. ## Context -Versioning, changelog generation, and publication need one auditable path. +Versioning, changelog generation, and publication need a shared, auditable path. ## Decision -Changesets and a split GitHub Actions workflow version, build, pack, and publish the root package -through npm trusted publishing with provenance. The generated changelog keeps pull-request and -commit links. Private examples remain outside versioning, and releases run only in CI. +Changesets and a split GitHub Actions workflow version, build, pack, and publish the root package. +Publication uses npm trusted publishing with provenance. The generated changelog keeps links to +pull requests and commits. Private examples remain outside versioning. Releases run only in CI. ## Consequences -Before initial publication, changes accumulate without Changesets. Prepare a single initial release -candidate when publication is authorized. +Before initial publication, changes accumulate without Changesets. When publication is authorized, +prepare a single initial release candidate. After initial publication, changes to published behavior, types, exports, dependency ranges, or installation metadata require a non-empty changeset. Documentation, examples, tests, internal -tooling, and CI require none. Any bootstrap token is removed after trusted publishing is configured. +tooling, and CI require none. After configuring trusted publishing, remove any bootstrap token. diff --git a/docs/adr/0017-quarantine-new-dependency-releases-for-seven-days.md b/docs/adr/0017-quarantine-new-dependency-releases-for-seven-days.md index 19d2215..6d9cf32 100644 --- a/docs/adr/0017-quarantine-new-dependency-releases-for-seven-days.md +++ b/docs/adr/0017-quarantine-new-dependency-releases-for-seven-days.md @@ -4,17 +4,17 @@ Status: Accepted. ## Context -Immediate dependency upgrades provide too little time to observe supply-chain incidents. +Upgrading dependencies immediately leaves too little time to observe supply-chain incidents. ## Decision pnpm enforces a strict seven-day minimum release age, frozen lockfiles in CI, and explicit -lifecycle-script permissions. Missing registry publication times fail closed. The same native -workspace settings apply to isolated consumers; frozen and cached installs remain subject to -registry publication metadata. +permissions for lifecycle scripts. Missing registry publication times fail closed. The same native +workspace settings apply to isolated consumers. Frozen and cached installs still require registry +publication metadata. ## Consequences Every registry version becomes eligible seven days after publication. Release-age exceptions, exclusions, configuration overrides, alternate installers, and missing-time bypasses are forbidden. -Already-installed dependencies may be reused read-only while an ineligible version remains blocked. +While an ineligible version remains blocked, already-installed dependencies can be reused read-only. diff --git a/docs/adr/0018-add-infinite-query-builders-for-unary-rpcs.md b/docs/adr/0018-add-infinite-query-builders-for-unary-rpcs.md index 6bd0464..50fc7cc 100644 --- a/docs/adr/0018-add-infinite-query-builders-for-unary-rpcs.md +++ b/docs/adr/0018-add-infinite-query-builders-for-unary-rpcs.md @@ -2,11 +2,11 @@ Status: Accepted. Partially supersedes ADR 0002 through ADR 0005 and ADR 0008. -Unary RPC leaves expose `infiniteKey` and `infiniteOptions` so pagination shares ordinary queries' -key and execution contracts. The mapper converts each `pageParam` to payload constructor input; -the mapped `initialPageParam` determines cache identity. Every page uses the Effect runner and +Unary RPC leaves expose `infiniteKey` and `infiniteOptions`. Pagination shares the key and execution +contracts of ordinary queries. The mapper converts each `pageParam` to payload constructor input. +The mapped `initialPageParam` determines cache identity. Every page uses the Effect runner and abort signal and satisfies ADR 0014's payload-stability requirement. -Payload-bearing queries accept `skipToken` as the exact `input`; payloadless RPCs omit the mapper. -Keys and failures use the `infinite` discriminator, and failures preserve the complete Cause. -This extends the earlier builder names, options, keys, and operation metadata. +Payload-bearing queries accept `skipToken` as the exact `input`. Payloadless RPCs omit the mapper. +Keys and failures use the `infinite` discriminator. Failures preserve the complete Cause. +These additions extend the earlier builder names, options, keys, and operation metadata. diff --git a/docs/adr/0019-host-effect-rpc-inside-tanstack-start.md b/docs/adr/0019-host-effect-rpc-inside-tanstack-start.md index 63b48e2..6a0920f 100644 --- a/docs/adr/0019-host-effect-rpc-inside-tanstack-start.md +++ b/docs/adr/0019-host-effect-rpc-inside-tanstack-start.md @@ -2,7 +2,7 @@ Status: Accepted. Supersedes part of ADR 0013. -Start hosts the standard Effect RPC HTTP protocol at its own exact `POST /rpc` route to demonstrate -the full-stack integration. A host-neutral Web handler shares the implementation with the -standalone Node server, which continues to serve Vite React. The examples exercise separate -hosting adapters; ADR 0013's shared-host requirement no longer applies. +Start hosts the standard Effect RPC HTTP protocol at its own exact `POST /rpc` route. This +demonstrates the full-stack integration. A host-neutral Web handler shares the implementation with +the standalone Node server, which continues to serve Vite React. The examples exercise separate +hosting adapters. ADR 0013's shared-host requirement no longer applies. diff --git a/docs/adr/0020-generate-accumulated-and-live-stream-queries.md b/docs/adr/0020-generate-accumulated-and-live-stream-queries.md index cb06ea3..359e28a 100644 --- a/docs/adr/0020-generate-accumulated-and-live-stream-queries.md +++ b/docs/adr/0020-generate-accumulated-and-live-stream-queries.md @@ -4,18 +4,18 @@ Status: Accepted. Partially supersedes ADR 0002 and ADR 0003. Extended to HTTP S [ADR 0024](0024-project-http-streams-and-buffered-metadata.md). Streams support two cached representations: ordered accumulated history and the latest live value. -Keep these representations separate, and include retention and refetch policies in accumulated -cache identity, because incompatible histories cannot safely share an entry. Broad key prefixes -still support invalidation across views and policies. +Keep these representations separate. Include retention and refetch policies in accumulated cache +identity because incompatible histories cannot safely share an entry. Broad key prefixes still +support invalidation across views and policies. -The adapter consumes caller-owned ready clients and follows TanStack cancellation while preserving -complete Effect failures. Applications own transport, reconnection, and resource lifetime; server -rendering must settle and stop open work before disposing those resources. Live values follow -[ADR 0012](0012-normalize-undefined-query-success-to-null.md), while accumulated elements retain -their decoded representation. +The adapter consumes caller-owned ready clients and follows TanStack cancellation. It preserves +complete Effect failures. Applications own transport, reconnection, and resource lifetime. Before +disposing those resources, server rendering must settle and stop open work. Live values follow +[ADR 0012](0012-normalize-undefined-query-success-to-null.md). Accumulated elements retain their +decoded representation. Expose a framework-neutral snapshot helper because native query cancellation can settle before -local iterator finalizers finish. A fresh capture exclusively owns an idle, unobserved exact query; -explicit cached capture can reuse successful data without acquiring work. Applications retain -client and runtime ownership and dispose them only after local cleanup settles. Browser +local iterator finalizers finish. A fresh capture exclusively owns an idle, unobserved exact query. +Explicit cached capture can reuse successful data without acquiring work. Applications retain +client and runtime ownership. Dispose those resources only after local cleanup settles. Browser reconnection uses independently owned resources. diff --git a/docs/adr/0021-share-utility-construction-through-private-modules.md b/docs/adr/0021-share-utility-construction-through-private-modules.md index ea37168..2a7346d 100644 --- a/docs/adr/0021-share-utility-construction-through-private-modules.md +++ b/docs/adr/0021-share-utility-construction-through-private-modules.md @@ -2,10 +2,10 @@ Status: Accepted. Clarifies ADR 0003, ADR 0005 through ADR 0007, and ADR 0009. Extended by [ADR 0022](0022-add-buffered-http-utilities-with-separate-key-roots.md) and [ADR 0023](0023-expose-buffered-multipart-http-mutations.md). -Share validation, canonical keys, options, and execution through private core modules. Adapters -own their protocol declarations, request preparation, ready-client invocation, and errors. Shared -Query Core contracts remain independent of protocol declarations. +Private core modules share validation, canonical keys, options, and execution. Adapters own their +protocol declarations, request preparation, ready-client invocation, and errors. Shared Query Core +contracts remain independent of protocol declarations. -This boundary gives common cache behavior one implementation while preserving each adapter's -input and execution semantics. Consumers use public factories from one root package; internal -modules and adapter registration remain private. +This boundary gives common cache behavior one implementation and preserves each adapter's input +and execution semantics. Consumers use public factories from one root package. Internal modules +and adapter registration remain private. diff --git a/docs/adr/0022-add-buffered-http-utilities-with-separate-key-roots.md b/docs/adr/0022-add-buffered-http-utilities-with-separate-key-roots.md index 2048370..5f448b1 100644 --- a/docs/adr/0022-add-buffered-http-utilities-with-separate-key-roots.md +++ b/docs/adr/0022-add-buffered-http-utilities-with-separate-key-roots.md @@ -3,10 +3,11 @@ Status: Accepted. Multipart support amended by [ADR 0023](0023-expose-buffered-multipart-http-mutations.md). Streaming and metadata views extended by [ADR 0024](0024-project-http-streams-and-buffered-metadata.md). -Expose HTTP and RPC utilities through one package, sharing private construction while preserving -their distinct declaration and request contracts. HTTP utilities consume caller-owned ready clients -and project only response shapes they can represent truthfully. +Expose HTTP and RPC utilities through one package. Share private construction and preserve each +adapter's declaration and request contracts. HTTP utilities consume caller-owned ready clients and +project only response shapes that they can represent accurately. -HTTP cache roots include the adapter and API identity, so HTTP endpoints and RPC tags cannot collide -under a shared caller prefix. Applications still own identity partitions and deliberate invalidation -across adapters. Decoded header wrappers and complete Effect failures remain intact at the boundary. +HTTP cache roots include the adapter and API identity. HTTP endpoints and RPC tags therefore cannot +collide under a shared caller prefix. Applications still own identity partitions and deliberate +invalidation across adapters. The boundary preserves decoded header wrappers and complete Effect +failures. diff --git a/docs/adr/0023-expose-buffered-multipart-http-mutations.md b/docs/adr/0023-expose-buffered-multipart-http-mutations.md index eed0991..3dac21d 100644 --- a/docs/adr/0023-expose-buffered-multipart-http-mutations.md +++ b/docs/adr/0023-expose-buffered-multipart-http-mutations.md @@ -3,12 +3,12 @@ Status: Accepted. Amends [ADR 0022](0022-add-buffered-http-utilities-with-separate-key-roots.md) and extends [ADR 0021](0021-share-utility-construction-through-private-modules.md). Extended HTTP views in [ADR 0024](0024-project-http-streams-and-buffered-metadata.md) retain this multipart capability limit. Buffered multipart uploads need mutation execution without query cache identity for files or -`FormData`. Expose only `key()`, `mutationKey()`, and `mutationOptions()` when an endpoint has any -buffered multipart payload alternative and all its successes are buffered. Preserve Effect's client +`FormData`. If an endpoint has any buffered multipart payload alternative and all successes are +buffered, expose only `key()`, `mutationKey()`, and `mutationOptions()`. Preserve Effect's client request shape, including `FormData` for multipart payloads and the upstream union for mixed payload alternatives. Any streaming success or streaming multipart payload still omits the whole endpoint. -Mutation-only endpoints require no key encoder and reject configured encoders because their +Mutation-only endpoints require no key encoder. They reject configured encoders because their variables never enter cache identity. They retain decoded-only responses, caller-owned runners and -resources, typed callbacks, and complete failed-Exit Causes. HTTP method does not change this +resources, typed callbacks, and complete failed-Exit Causes. The HTTP method does not change this capability boundary. diff --git a/docs/adr/0024-project-http-streams-and-buffered-metadata.md b/docs/adr/0024-project-http-streams-and-buffered-metadata.md index 58f6f4a..ee6242e 100644 --- a/docs/adr/0024-project-http-streams-and-buffered-metadata.md +++ b/docs/adr/0024-project-http-streams-and-buffered-metadata.md @@ -3,12 +3,12 @@ Status: Accepted. Amends [ADR 0020](0020-generate-accumulated-and-live-stream-queries.md) and [ADR 0022](0022-add-buffered-http-utilities-with-separate-key-roots.md). -Query caches hold decoded application values, while HTTP responses and streams retain transport -resources. Generate accumulated and live views for unambiguous SSE successes, and an opt-in -buffered metadata view containing decoded data, status, and a plain header snapshot. Give each +Query caches hold decoded application values. HTTP responses and streams retain transport +resources. Generate accumulated and live views for unambiguous SSE successes. Generate an opt-in +buffered metadata view with decoded data, status, and a plain header snapshot. Give each representation separate cache identity so incompatible data shapes cannot share an entry. -Preserve declared decoded header wrappers. Omit mixed buffered/streamed successes and raw byte -streams: the former lack one truthful view, and the latter have transport-dependent chunk -boundaries. Applications own download sinks, reconnection, resume policy, and client lifetime; -the adapter consumes their ready clients without caching transport resources. +Preserve declared decoded header wrappers. Omit mixed buffered/streamed successes because they lack +one accurate view. Omit raw byte streams because their chunk boundaries depend on the transport. +Applications own download sinks, reconnection, resume policy, and client lifetime. The adapter +consumes their ready clients without caching transport resources. diff --git a/docs/adr/0025-use-public-decoded-message-clients-for-server-local-rpc.md b/docs/adr/0025-use-public-decoded-message-clients-for-server-local-rpc.md index 458daeb..7f21722 100644 --- a/docs/adr/0025-use-public-decoded-message-clients-for-server-local-rpc.md +++ b/docs/adr/0025-use-public-decoded-message-clients-for-server-local-rpc.md @@ -3,12 +3,13 @@ Status: Accepted. Extends [ADR 0007](0007-keep-the-core-factory-lifecycle-neutral.md) and [ADR 0019](0019-host-effect-rpc-inside-tanstack-start.md). -Server rendering can execute local handlers without a network round trip. Applications may -connect Effect's public decoded-message client and server within a request-owned Scope and pass -the ready client to the adapter. This preserves the lifecycle-neutral boundary without adding a -package-owned client factory or relying on test constructors. +Server rendering can execute local handlers without a network round trip. Applications can connect +Effect's public decoded-message client and server within a request-owned Scope, then pass the ready +client to the adapter. This preserves the lifecycle-neutral boundary. It requires neither a +package-owned client factory nor test constructors. -Server-owned services determine request authority; caller execution services cannot override it. -Each request owns its connection and QueryClient, and browser execution owns separate resources. -Decoded-message execution skips transport Schema codecs, so applications requiring wire validation -or codec effects retain a schema-aware protocol. The exact Effect peer bounds this upstream API. +Server-owned services determine request authority. Caller execution services cannot override that +authority. Each request owns its connection and QueryClient. Browser execution owns separate +resources. Decoded-message execution skips transport Schema codecs. If wire validation or codec +effects are required, applications retain a schema-aware protocol. The exact Effect peer bounds +this upstream API. diff --git a/docs/adr/0026-keep-rpc-transport-aggregation-in-the-application.md b/docs/adr/0026-keep-rpc-transport-aggregation-in-the-application.md index 43a9f80..6dd22a4 100644 --- a/docs/adr/0026-keep-rpc-transport-aggregation-in-the-application.md +++ b/docs/adr/0026-keep-rpc-transport-aggregation-in-the-application.md @@ -2,10 +2,10 @@ Status: Accepted. Extends [ADR 0007](0007-keep-the-core-factory-lifecycle-neutral.md). -Repeated RPC calls can incur substantial transport overhead. Keep transport selection and -aggregation in the application because they determine connection lifetime, latency, routing, -retries, and cancellation. Shared WebSocket transport reduces repeated headers while preserving -independent calls; explicit bulk RPCs remain a domain choice. +Repeated RPC calls can incur substantial transport overhead. Transport selection and aggregation +determine connection lifetime, latency, routing, retries, and cancellation. Keep both in the +application. Shared WebSocket transport reduces repeated headers and preserves independent calls. +Explicit bulk RPCs remain a domain choice. The utility tree adds no automatic transport batching. Backend request batching, transport aggregation, and Query publication address different costs and retain separate ownership. diff --git a/skills/effect-api-query/SKILL.md b/skills/effect-api-query/SKILL.md index d11b158..d258089 100644 --- a/skills/effect-api-query/SKILL.md +++ b/skills/effect-api-query/SKILL.md @@ -49,126 +49,129 @@ sources: # Use effect-api-query -Derive options from the application's contract and ready client, then pass them -to TanStack Query. This package owns utility construction and query execution; -the application owns transport, authentication, resources, and cache policy. +Derive options from the application's contract and ready client. Pass those options +to TanStack Query. The package owns utility construction and query execution. +The application owns transport, authentication, resources, and cache policy. ## Choose the adapter -Inspect the installed package manifest for its version and peers. This version -uses the coordinated Effect 4.0.0 set; Query Core peers are `>=5.103.1 <6`. -Use the installed declarations and preserve literal contracts. -[Frameworks and hosts](references/frameworks-and-hosts.md) explains reactive -options, version constraints, and transport ownership. +Read the installed package manifest to find its version and peers. This version +uses the coordinated Effect 4.0.0 set. Its Query Core peer range is `>=5.103.1 <6`. +Use the installed declarations and keep contracts literal. For reactive options, +version constraints, and transport ownership, read +[frameworks and hosts](references/frameworks-and-hosts.md). Read the reference for the contract you are integrating: -| Contract | Factory and input | Reference | -| ---------------- | ------------------------------------------------ | ------------------------------------------ | -| Effect RPC group | `createRpcQueryUtils`; payload constructor input | [RPC setup and rules](references/rpc.md) | -| Effect HttpApi | `createHttpApiQueryUtils`; decoded request parts | [HTTP setup and rules](references/http.md) | +| Contract | Factory and input | Reference | +| ---------------- | ---------------------------------------------------- | ------------------------------------------ | +| Effect RPC group | `createRpcQueryUtils` with payload constructor input | [RPC setup and rules](references/rpc.md) | +| Effect HttpApi | `createHttpApiQueryUtils` with decoded request parts | [HTTP setup and rules](references/http.md) | -Import both factories and error guards from `effect-api-query`. The package has -one public root export. A factory accepts an already acquired client and builds +Import both factories and error guards from `effect-api-query`, the package's one +public root export. Each factory accepts an already acquired client. It builds an eager, frozen utility tree without making a request. ## Choose the operation -| Need | Builder | TanStack consumer | -| -------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------- | -| Buffered read | `queryOptions({ input, ...options })` | `useQuery`, `useSuspenseQuery`, `queryClient.query` | -| Write | `mutationOptions({ ...callbacks })` | `useMutation`, `MutationObserver`; pass variables to `mutate` | -| Cursor pagination | `infiniteOptions({ initialPageParam, input, getNextPageParam })` | `useInfiniteQuery`, `queryClient.infiniteQuery` | -| HTTP data/status/headers | `metadataOptions({ input, ...options })` | Ordinary query hooks or observers | -| Ordered RPC or SSE history | `streamedOptions({ input, maxChunks, refetchMode })` | Ordinary query hooks or observers | -| Latest RPC or SSE value | `liveOptions({ input })` | Ordinary query hooks or observers | - -Unary RPCs and buffered HTTP endpoints without multipart expose read, write, and pagination builders. -Choose by application intent; HTTP method and RPC name do not restrict the choice. -Buffered multipart HTTP endpoints expose mutation builders only and accept explicit `FormData`; -follow the [HTTP rules](references/http.md) for upload input and encoder configuration. -Streaming RPCs and supported HTTP SSE endpoints expose accumulated and live -builders. Metadata is a separate buffered HTTP query view. Inputless operations -omit `input`. +| Need | Builder | TanStack consumer | +| ------------------------------ | ---------------------------------------------------------------- | -------------------------------------------------------------------- | +| Buffered read | `queryOptions({ input, ...options })` | `useQuery`, `useSuspenseQuery`, `queryClient.query` | +| Write | `mutationOptions({ ...callbacks })` | `useMutation`, `MutationObserver`, with variables passed to `mutate` | +| Cursor pagination | `infiniteOptions({ initialPageParam, input, getNextPageParam })` | `useInfiniteQuery`, `queryClient.infiniteQuery` | +| HTTP data, status, and headers | `metadataOptions({ input, ...options })` | Ordinary query hooks or observers | +| Ordered RPC or SSE history | `streamedOptions({ input, maxChunks, refetchMode })` | Ordinary query hooks or observers | +| Latest RPC or SSE value | `liveOptions({ input })` | Ordinary query hooks or observers | + +Unary RPCs and buffered HTTP endpoints without multipart expose read, write, and +pagination builders. Choose the builder for the application's intent. Neither +the HTTP method nor the RPC name restricts that choice. + +Buffered multipart HTTP endpoints expose only mutation builders and accept +explicit `FormData`. For upload input and encoder configuration, follow the +[HTTP rules](references/http.md). Streaming RPCs and supported HTTP SSE endpoints +expose accumulated and live builders. Metadata provides a separate buffered HTTP +query view. Inputless operations omit `input`. Read only the branch needed for the task: -| Task | Read | -| ------------------------------------------------------------------------------ | ---------------------------------------------------------- | -| Infer reusable operation types, skip input, paginate, or choose stream history | [Query patterns](references/query-patterns.md) | -| Reuse typed policies, filter caches, apply writes or events, replace owners | [Cache workflows](references/cache-workflows.md) | -| Capture open streams, serialize query views, or dispose request work | [Hydration and SSR](references/hydration-and-ssr.md) | -| Mount native providers, recover Suspense, choose transport/host or versions | [Frameworks and hosts](references/frameworks-and-hosts.md) | +| Task | Read | +| ------------------------------------------------------------------------------- | ---------------------------------------------------------- | +| Infer reusable operation types, skip input, paginate, or choose stream history | [Query patterns](references/query-patterns.md) | +| Reuse typed policies, filter caches, apply writes or events, replace owners | [Cache workflows](references/cache-workflows.md) | +| Capture open streams, serialize query views, or dispose request work | [Hydration and SSR](references/hydration-and-ssr.md) | +| Mount native providers, recover Suspense, choose a transport, host, or versions | [Frameworks and hosts](references/frameworks-and-hosts.md) | ## Preserve generated cache identity -- Supply a non-empty JSON tuple as `keyPrefix`. Include safe user, tenant, or - backend identifiers when they distinguish results. The factory cannot infer +- Supply a non-empty JSON tuple as `keyPrefix`. If safe user, tenant, or backend + identifiers distinguish results, include them. The factory cannot infer identity from a client, URL, middleware, or RPC request options. -- Pass caller options such as `staleTime`, `select`, and callbacks into the - builder. Preserve its `queryFn` and `queryKey`. Configure custom hashing through - QueryClient global or prefix `queryKeyHashFn`; builders reject per-call hashing. - Keep hashing stable while cache entries remain. Streams reject a default - `queryHash` inconsistent with the key hasher or a conflicting cached identity - with `UnsupportedQueryHash`. Retire the old cache before changing its hash - policy. Mutation builders own `mutationFn` and `mutationKey`. -- Treat captured inputs as immutable and build new options when they change. - Keys are frozen snapshots; request inputs are not necessarily copied or frozen, - so later mutation can make execution disagree with its key. +- Pass options such as `staleTime`, `select`, and callbacks into the builder. + Preserve its `queryFn` and `queryKey`. To customize hashing, use QueryClient + global or prefix `queryKeyHashFn`. Builders reject per-call hashing. Keep hashing + stable while cache entries remain. Streams reject a default `queryHash` + inconsistent with the key hasher or a conflicting cached identity with + `UnsupportedQueryHash`. + Before changing the hash policy, retire the old cache. Mutation builders own + `mutationFn` and `mutationKey`. +- Treat captured inputs as immutable. When inputs change, build new options. + Keys are frozen snapshots, but request inputs are not necessarily copied or + frozen. Mutating an input later can make execution disagree with its key. - Use the generated concrete key for its cache shape. Accumulated keys include - normalized retention/refetch policy: `streamedKey(input, policy)`. HTTP stream + normalized retention and refetch policy: `streamedKey(input, policy)`. HTTP stream keys also include decoder policy. Inputless stream keys take policy first. Concrete keys retain the unselected cache data type. Hooks, observers, and - `queryClient.query` apply `select`; `getQueryData` reads the decoded cache value. - Use the matching options' `queryKey` when it already exists. -- Use `key()` on a tree, branch, or leaf for prefix invalidation. After mutations, - invalidate affected reads explicitly. RPC and HTTP roots are distinct; use - the original caller prefix only for deliberate invalidation across adapters. + `queryClient.query` apply `select`. `getQueryData` reads the decoded cache value. + If matching options already exist, use their `queryKey`. +- For prefix invalidation, use `key()` on a tree, branch, or leaf. After mutations, + explicitly invalidate affected reads. RPC and HTTP roots are distinct. Use the + original caller prefix only for deliberate invalidation across adapters. ## Own execution and cleanup -Keep the client's Scope or ManagedRuntime alive for every operation that uses it. An RPC -client returned from an already completed `Effect.scoped` region is no longer -usable. Create utilities when setting up that client and reuse them across component renders. +Keep the client's Scope or ManagedRuntime alive through every operation that uses +it. An RPC client returned from a completed `Effect.scoped` region is unusable. +During client setup, create utilities and reuse them across component renders. -When execution requires client or Schema services, pass a `runPromiseExit` runner -that provides them. It must return an Effect `Exit` and forward its second argument's -`signal`; `runtime.runPromiseExit` has this shape. Service-free calls can use the -factory default. A custom key encoder supplies cache identity; execution services -still come from the runner. +If execution requires client or Schema services, pass a `runPromiseExit` runner +that provides them. The runner must return an Effect `Exit` and forward the +`signal` from its second argument. `runtime.runPromiseExit` has this shape. +Service-free calls can use the factory default. A custom key encoder supplies +cache identity. The runner still supplies execution services. Ordinary queries forward TanStack cancellation to Effect. For Suspense hooks, read the cancellation limitation in [frameworks and hosts](references/frameworks-and-hosts.md). -Stop consumers, cancel queries, -and drain local streams, pending preparation, and accepted mutations before -disposing resources. Native cancellation can settle before finalizers finish; -`fetchStreamSnapshot` drains its own capture. Mutations receive no query abort -signal. A cancellable command needs an application operation identified before -work starts; owner retirement leaves completed remote work intact. - -For spans and cleanup that cover stream consumption, follow the lifetime tracing +Before disposing resources, stop consumers, cancel queries, and drain local streams, +pending preparation, and accepted mutations. Native cancellation can settle before +finalizers finish. `fetchStreamSnapshot` drains its own capture. Mutations receive +no query abort signal. A cancellable command needs an application operation +identified before work starts. Owner retirement leaves completed remote work intact. + +To trace spans and cleanup through stream consumption, follow the lifetime tracing recipe in [query patterns](references/query-patterns.md). ## Interpret results and failures Buffered queries and live emissions normalize successful `undefined` to `null`. -Accumulated stream elements and mutation results preserve `undefined`. See the -query patterns reference for stream behavior. +Accumulated stream elements and mutation results preserve `undefined`. For stream +behavior, read the query patterns reference. Configuration and key-generation errors can fail preparation before a hook or -query function runs. A stream's incompatible cache hashing rejects execution with -a configuration error. Failed execution Exits become -`EffectRpcQueryError` or `EffectHttpApiQueryError`. Narrow with -`isEffectRpcQueryError` or `isEffectHttpApiQueryError`, then inspect the preserved -Effect `cause`. A runner rejection passes through unchanged. +query function runs. Incompatible stream cache hashing rejects execution with a +configuration error. Failed execution Exits become `EffectRpcQueryError` or +`EffectHttpApiQueryError`. Use `isEffectRpcQueryError` or `isEffectHttpApiQueryError` +to narrow the error, then inspect its preserved Effect `cause`. Runner rejections +pass through unchanged. -The Cause is not sanitized and can contain upstream request or Schema values. -Expose application-safe error information and apply the application's logging -policy to the Cause. +The Cause can contain upstream request or Schema values and receives no sanitization. +Expose error information that is safe for the application. Apply the application's +logging policy to the Cause. ## Verify the integration -Type-check against the installed package with strict TypeScript settings. Check -that the actual contract produces the intended utility path and input shape. -Exercise the affected read or write and its cache behavior; for identity or -lifetime changes, verify separation between identities and cleanup on disposal. +Type-check against the installed package with strict TypeScript settings. Make +sure that the contract produces the intended utility path and input shape. +Exercise the affected read or write and its cache behavior. For identity or +lifetime changes, make sure that identities remain separate and disposal completes cleanup. +Finish only when these checks pass. diff --git a/skills/effect-api-query/references/cache-workflows.md b/skills/effect-api-query/references/cache-workflows.md index 53cb202..a3b35b2 100644 --- a/skills/effect-api-query/references/cache-workflows.md +++ b/skills/effect-api-query/references/cache-workflows.md @@ -2,12 +2,12 @@ ## Register native defaults and filters -Create the QueryClient and utilities once for each owner. Register global defaults, -then matching prefixes from broad to specific; TanStack merges them in registration -order, without sorting by specificity. Omit an option to inherit it. Explicit -`initialData: undefined` overrides an inherited seed, and `staleTime: 0` overrides -inherited freshness. Reuse endpoint-aware callbacks through typed policies; keep -selection and input at each builder call. +Create the QueryClient and utilities once per owner. Register global defaults, +then matching prefixes from broad to specific. TanStack merges defaults in +registration order without sorting by specificity. To inherit an option, omit it. +Explicit `initialData: undefined` overrides an inherited seed. `staleTime: 0` +overrides inherited freshness. Reuse endpoint-aware callbacks through typed +policies. Keep selection and input at each builder call. ```ts import { QueryClient } from '@tanstack/query-core' @@ -46,33 +46,38 @@ export function userCache(client: RpcClient.RpcClient.Flat` or -`MutationPolicy` to retain contextual callback types. Query callbacks -see that view's cached data and error; mutation callbacks see its data and complete -variables. Input, projection, initial/placeholder data, page mapping, stream -identity, and adapter request controls belong to each bound builder invocation. -QueryClient continues to own hashing; generated functions and keys stay owned. - -Mutation policies exclude `onMutate` so local optimistic context retains its -inference. Shared callbacks receive that local result as `unknown`. Call-site -options replace matching defaults, including whole callbacks; callbacks are not -composed. Compose explicitly if both actions are required, and return their -Promise when settlement must wait. Keep originating-owner checks and overlapping -write coordination in the application workflow below. For typed domain failure -classification and shared RPC/HTTP retry decisions, use +To retain contextual callback types in a stored policy, use +`satisfies QueryPolicy` or `MutationPolicy`. +Query callbacks receive the view's cached data and error. Mutation callbacks +receive its data and complete variables. Input, projection, initial and placeholder +data, page mapping, stream identity, and adapter request controls stay local to +each bound builder invocation. QueryClient still owns hashing. The library still +owns generated functions and keys. + +Mutation policies exclude `onMutate` to preserve inference for local optimistic +context. Shared callbacks receive that local result as `unknown`. Call-site options +replace matching defaults, including whole callbacks. The helpers do not compose +callbacks. If both actions are required, compose them explicitly. If settlement +must wait, return their Promise. + +Keep checks of the originating owner and coordination of overlapping writes in +the application workflow below. For typed domain failure classification and +shared RPC/HTTP retry decisions, use [the retry recipe](https://ueberbrot.github.io/effect-api-query/guides/retry-queries/). ## Coordinate overlapping optimistic writes -Keep this workflow in the application and make every participating write follow it: +The application owns this workflow. Make every participating write follow these +steps: -1. In `onMutate`, capture the originating owner and reserve this write before an - await. Cancel older reconciliation reads before publishing optimistic state. +1. In `onMutate`, capture the originating owner and reserve this write before any + await. Before publishing optimistic state, cancel older reconciliation reads. 2. Snapshot only the affected entities and positions. Preserve unrelated list - entries, infinite pages, and `pageParams`; allocate temporary IDs per creation. - Each new deletion captures current fields and positions after an authoritative - refresh; shared overlap snapshots retain ordering within the owned view. + entries, infinite pages, and `pageParams`. Allocate temporary IDs per creation. + After an authoritative refresh, each new deletion captures current fields and + positions. Shared overlap snapshots retain ordering within the owned view. 3. Return the original owner's commit, rollback, and settlement actions in mutation - context. Check that owner after every await and before any cache side effect. -4. On success, replace only this write's optimistic value and seed exact generated + context. After every await and before any cache side effect, check that owner. +4. On success, replace only this write's optimistic value. Seed exact generated detail keys with the response's locale and other identity fields. 5. On failure, remove this write's temporary entry or restore its removed entity - beside surviving original neighbors. A whole stale-snapshot rollback can erase - another write. Roll back only a cache revision still owned by the optimistic - write; preserve later authoritative updates even when structural sharing keeps - the same data object. Coordinate repeated deletions of the same entity explicitly. -6. Reconcile when the final overlapping write settles. Each new write cancels an - earlier reconciliation before its optimistic update. - -An observer's option update can replace pending callbacks or a not-yet-started -mutation function. Context preserves originating actions; remount consumers at an -owner handoff. Cache rollback cannot undo accepted server work. External writes, -manual replacement, and server ordering need application conflict policy. + beside surviving original neighbors. Rolling back a whole stale snapshot can + erase another write. Roll back only a cache revision that the optimistic write + still owns. Preserve later authoritative updates even when structural sharing + keeps the same data object. Explicitly coordinate repeated deletions of the + same entity. +6. When the final overlapping write settles, reconcile. Each new write cancels an + earlier reconciliation before publishing its optimistic update. + +An observer's option update can replace pending callbacks or a mutation function +that has not started. Context preserves originating actions. At an owner handoff, +remount consumers. Cache rollback cannot undo accepted server work. External writes, +manual replacement, and server ordering require an application conflict policy. ## Refresh from application events -Map authenticated, decoded domain events deliberately to native generated filters. +Explicitly map authenticated, decoded domain events to native generated filters. If RPC and HTTP share a domain, name both roots. Capture the original owner in one -scoped consumer; unregister event, Reactivity, and mutation listeners on closure. -Check that owner before queued invalidation and after awaits. - -Defer refresh while participating mutations are pending, retain the invalidation -intent, and flush it after settlement. A branch refresh may subsume narrower work. -Effect Reactivity keys need an explicit application mapping; a Query tuple has no -implied Reactivity subscription. Native batching combines invalidations of the same -Reactivity key, without guaranteeing one Query refresh across distinct keys. - -An accepted monotonic cursor records invalidation intent, not durable delivery or -successful reads. Replay ordered events with duplicate suppression when supported; -otherwise refresh authoritative reads and reset the sequence deliberately. The -application owns authentication, reconnect, replay storage, epochs, acknowledgements, -and out-of-order delivery. +scoped consumer. On closure, unregister event, Reactivity, and mutation listeners. +Before queued invalidation and after awaits, check that owner. + +While participating mutations are pending, defer refresh and retain the invalidation +intent. After settlement, flush that intent. A branch refresh can cover narrower +work. Effect Reactivity keys need an explicit application mapping. A Query tuple +does not imply a Reactivity subscription. Native batching combines invalidations +of the same Reactivity key. It does not guarantee one Query refresh across +distinct keys. + +An accepted monotonic cursor records invalidation intent. It does not establish +durable delivery or successful reads. If supported, replay ordered events with +duplicate suppression. Otherwise, refresh authoritative reads and explicitly reset +the sequence. The application owns authentication, reconnect, replay storage, +epochs, acknowledgments, and out-of-order delivery. ## Replace an owner in order Partition results by safe tenant, user, backend, session, and permission-generation -identity in `keyPrefix`; keep credentials out of keys. Capture the retiring owner, -unmount its consumers, mark it inactive, stop new work, cancel queries, and drain -accepted mutations and local streams before releasing ready-client resources. -Only then acquire and mount the replacement. Apply this sequence even when semantic -identity is unchanged. A provider client prop change alone does not replace hook -ownership. Guard late commit, rollback, and settlement callbacks even after drain. - -An accepted command may finish remotely during retirement; completed work persists. -Use an explicit application command-cancellation operation to stop future domain -work. Local retirement supplies neither rollback nor remote cancellation acknowledgement. -A hanging request or finalizer can delay handoff. For an operation-ID protocol with -separate start, progress, and cancel calls, use the +identity in `keyPrefix`. Keep credentials out of keys. Capture the retiring owner, +then unmount its consumers. Mark it inactive and stop new work. Cancel queries and +drain accepted mutations and local streams before releasing ready-client resources. +Only then acquire and mount the replacement. + +Apply this sequence even when semantic identity stays the same. Changing a provider +client prop alone does not replace hook ownership. Guard late commit, rollback, +and settlement callbacks even after draining work. + +An accepted command can finish remotely during retirement. Completed work persists. +To stop future domain work, use an explicit application command-cancellation operation. +Local retirement supplies neither rollback nor acknowledgment of remote cancellation. +A hanging request or finalizer can delay handoff. For an operation-ID protocol +with separate start, progress, and cancel calls, use the [cancellable commands example](https://ueberbrot.github.io/effect-api-query/examples/cancellable-commands/). -For reload persistence, allowlist a validated DTO view, repeat the safe owner tuple -in its envelope, and include schema/key version busters. Restore only matching -identities through freshly generated keys; discard foreign or incompatible data -before writing. Avoid persisting pending optimistic values, arbitrary caches, errors, -mutations, credentials, or class prototypes. Use paired codecs for rich values. - -Explicit disposal should expose storage failures while completing resource cleanup. -Lifecycle callbacks such as pagehide or HMR must handle a rejected disposal Promise -so it does not become an unhandled rejection. Preserve the chosen reload-persistence -policy and ordered cleanup. +For reload persistence, allowlist a validated DTO view. Repeat the safe owner tuple +in its envelope and include version busters for schemas and keys. Restore only +matching identities through freshly generated keys. Before writing, discard foreign +or incompatible data. Avoid persisting pending optimistic values, arbitrary caches, +errors, mutations, credentials, or class prototypes. For rich values, use paired +codecs. + +During explicit disposal, expose storage failures while completing resource cleanup. +Lifecycle callbacks such as pagehide or HMR must handle a rejected disposal Promise. +Otherwise, it becomes an unhandled rejection. Preserve the chosen reload-persistence +policy and cleanup order. Sources: [defaults and policies](https://ueberbrot.github.io/effect-api-query/guides/query-defaults/), [cache keys and filters](https://ueberbrot.github.io/effect-api-query/guides/cache-filters/), diff --git a/skills/effect-api-query/references/frameworks-and-hosts.md b/skills/effect-api-query/references/frameworks-and-hosts.md index 57ddf6f..ddd9b0e 100644 --- a/skills/effect-api-query/references/frameworks-and-hosts.md +++ b/skills/effect-api-query/references/frameworks-and-hosts.md @@ -1,26 +1,27 @@ # Native frameworks, transports, and supported hosts -When choosing a framework, router, or host, use the -[integration overview](https://ueberbrot.github.io/effect-api-query/integrations/) -for native consumer and application lifetime boundaries. +To choose a framework, router, or host, read the +[integration overview](https://ueberbrot.github.io/effect-api-query/integrations/). +It describes native consumers and application lifetime boundaries. ## Rebuild captured options from reactive input -Acquire clients and create utilities during application setup. Supply the native -framework's Query provider. Builders capture plain input at each call; invoke them -inside the framework's reactive options computation so changed input produces a -new key and query function. Mutations receive new variables at execution time. +During application setup, acquire clients and create utilities. Supply the native +framework's Query provider. Builders capture plain input at each call. Invoke them +inside the framework's reactive options computation. Input changes then produce +a new key and query function. Mutations receive new variables at execution time. -| Framework | Native options construction | -| --------- | --------------------------------------------------------------------------------- | -| React | Rebuild the builder result as component input changes; use ordinary Query hooks | -| Vue | `computed(() => rpc.users.read.queryOptions(...))` passed to `useQuery` | -| Solid | `useQuery(() => rpc.users.read.queryOptions(...))` | -| Svelte | `createQuery(() => rpc.users.read.queryOptions(...))` | -| Angular | `injectQuery(() => rpc.users.read.queryOptions(...))` inside an injection context | +| Framework | Native options construction | +| --------- | ---------------------------------------------------------------------------------- | +| React | Rebuild the builder result as component input changes and use ordinary Query hooks | +| Vue | `computed(() => rpc.users.read.queryOptions(...))` passed to `useQuery` | +| Solid | `useQuery(() => rpc.users.read.queryOptions(...))` | +| Svelte | `createQuery(() => rpc.users.read.queryOptions(...))` | +| Angular | `injectQuery(() => rpc.users.read.queryOptions(...))` inside an injection context | -Apply this pattern to infinite, metadata, accumulated, and live options too. This -factory constructs utilities once and returns hooks for native component setup: +Use the same pattern for infinite, metadata, accumulated, and live options. +The following factory constructs utilities once and returns hooks for native +component setup: ```ts import { useQuery as useSolidQuery } from '@tanstack/solid-query' @@ -70,90 +71,95 @@ export function makeUserHooks(client: RpcClient.RpcClient.Flat boolean`, -instead of Core's `enabled(query)` callback. Solid accepts the Core callback. - -Solid Query 5.104.0 query/infinite overloads require a defined seed or no seed; a -possibly undefined value or initializer does not match. Vue accepts that initializer. -Explicit `initialData: undefined` overrides inherited seeds. Angular's skipped query -keeps optional `data()` even with defined initial data; Svelte preserves defined data -for that combination. Both accept possibly undefined initializers with optional data. - -Compile Svelte with `module: 'ESNext'` and `moduleResolution: 'Bundler'`; NodeNext -checking exposes an upstream `.svelte` declaration import limitation. Angular results -are signals such as `data()`; register `provideTanStackQuery(queryClient)` and use an -injection context. Use native infinite and mutation hooks with the matching builders. - -For ordinary Query hooks and observers, last-observer removal cancels abort-aware -open queries. Keep resources alive until local finalizers finish. For an owner -change, follow the ordered unmount, drain, dispose, and remount procedure in -[cache workflows](cache-workflows.md). +Vue's explicit hook generics preserve tagged data and error types. They avoid deep +recursive unwrapping of Effect Causes. Let builders infer their arguments. In Vue, +function-valued `enabled` is a zero-argument getter. Use a boolean or `() => boolean`. +Vue does not use Core's `enabled(query)` callback. Solid accepts that Core callback. + +Solid Query 5.104.0 query and infinite overloads require a defined seed or no seed. +A possibly undefined value or initializer does not match. Vue accepts that +initializer. Explicit `initialData: undefined` overrides inherited seeds. +Angular's skipped query keeps optional `data()` even with defined initial data. +Svelte preserves defined data for that combination. Angular and Svelte both accept +possibly undefined initializers with optional data. + +Compile Svelte with `module: 'ESNext'` and `moduleResolution: 'Bundler'`. NodeNext +checking exposes an upstream limitation with imports in `.svelte` declarations. +Angular results are signals such as `data()`. Register +`provideTanStackQuery(queryClient)` and use an injection context. Use native +infinite and mutation hooks with their matching builders. + +For ordinary Query hooks and observers, removing the last observer cancels +abort-aware open queries. Keep resources alive until local finalizers finish. +When changing an owner, follow the ordered unmount, drain, dispose, and remount +procedure in [cache workflows](cache-workflows.md). ## Mount a provider and recover Suspense -For a first React integration, acquire one client, create the utility tree and -QueryClient once, and mount `QueryClientProvider`. Unmount consumers before disposing -the application's client resources. Use -[the React client setup](https://ueberbrot.github.io/effect-api-query/guides/react-query/#acquire-and-mount-the-client) -for one acquired HTTP client, one component, and an awaited cleanup function. +For a first React integration, acquire one client. Create the utility tree and +QueryClient once, then mount `QueryClientProvider`. Before disposing the +application's client resources, unmount consumers. For one acquired HTTP client, +one component, and an awaited cleanup function, use +[the React client setup](https://ueberbrot.github.io/effect-api-query/guides/react-query/#acquire-and-mount-the-client). Put `QueryErrorResetBoundary` around a component error boundary and Suspense. -On retry, reset both the Query error boundary and the component boundary before -rendering its queries again. With cached data, a failed refetch normally keeps -rendering that data; show the local error when fetching stops, or deliberately -throw it if the application wants boundary recovery. - -Use `useSuspenseQueries` for independent reads that should start together. -Its native overload may need explicit `{ queryFnData, error }` type descriptors -derived with the inference helpers. Follow -[the complete Suspense recipe](https://ueberbrot.github.io/effect-api-query/guides/suspense-and-recovery/) -for the boundary and parallel-query signatures. Native Suspense query hooks do -not support cancellation: use ordinary Query hooks or observers when abort and -open-stream cleanup are required. Skipped query options cannot be used with -Suspense hooks. +On retry, reset both error boundaries before rendering the component's queries +again. With cached data, a failed refetch normally keeps rendering that data. +When fetching stops, show the local error. If the application wants boundary +recovery, explicitly throw the error. + +For independent reads that need to start together, use `useSuspenseQueries`. +Its native overload can require explicit `{ queryFnData, error }` type descriptors +derived with the inference helpers. For the boundary and parallel-query +signatures, follow +[the complete Suspense recipe](https://ueberbrot.github.io/effect-api-query/guides/suspense-and-recovery/). +Native Suspense query hooks do not support cancellation. If abort and open-stream +cleanup are required, use ordinary Query hooks or observers. Suspense hooks do +not accept skipped query options. ## Match application dependencies Keep Effect packages coordinated with the exact package peer. Match each native -Query wrapper to the application's Query Core and use a compiler supported by -its framework tooling. Angular core, common, compiler, and platform-browser -should share one release. +Query wrapper to the application's Query Core. Use a compiler that its framework +tooling supports. Keep Angular core, common, compiler, and platform-browser on the +same release. ## Keep transport and host ownership explicit -- **HTTP RPC:** acquire a flat client in a live application Scope with matching - protocol and serialization, as in [RPC setup](rpc.md). -- **Shared WebSocket RPC:** concurrent unary and stream requests share the owned - connection while retaining independent keys and cancellation. Acquire replacement - resources after retiring the prior owner. Reconnect, backoff, authentication, and - replay remain application policy. Local cancellation does not prove remote cleanup. -- **Server-local RPC:** a request-scoped public decoded-message client/server can - avoid a network round trip. Server-owned services determine authority; caller - services cannot override it. This path skips wire Schema codecs; retain a - schema-aware protocol when their validation or effects matter. -- **Browser/dedicated worker:** each host owns its ready clients, runtime, Scope, +The application owns the transport and host resources in each integration: + +- HTTP RPC requires a flat client in a live application Scope with matching + protocol and serialization. Follow [RPC setup](rpc.md) to acquire it. +- Shared WebSocket RPC lets concurrent unary and stream requests share the owned + connection. They retain independent keys and cancellation. After retiring the + prior owner, acquire replacement resources. The application owns reconnect, + backoff, authentication, and replay policy. Local cancellation does not prove + remote cleanup. +- Server-local RPC can avoid a network round trip through a request-scoped public + decoded-message client and server. Server-owned services determine authority. + Caller services cannot override it. This path skips wire Schema codecs. If their + validation or effects matter, retain a schema-aware protocol. +- Browser and dedicated worker hosts each own their ready clients, runtime, Scope, and QueryClient. Worker-hosted operations do not establish cross-host `postMessage` - transport, service/shared workers, authentication, or cache synchronization. - Await cleanup before terminating a worker. -- **External OpenAPI HTTP:** `openapi-typescript` can generate encoded DTO types + transport, service or shared workers, authentication, or cache synchronization. + Before terminating a worker, await cleanup. +- External OpenAPI HTTP can use `openapi-typescript` to generate encoded DTO types from native `OpenApi.fromApi` for `openapi-fetch`. That client calls HTTP - independently and cannot replace the ready Effect HttpApiClient in the factory. - Multipart needs explicit FormData serialization. Raw SSE bytes provide neither - event parsing nor Schema decoding or reserved-failure handling. + independently. It cannot replace the ready Effect HttpApiClient in the factory. + Multipart needs explicit FormData serialization. Raw SSE bytes provide no event + parsing, Schema decoding, or reserved-failure handling. -Keep the native WebSocket buffer default of 16 unless the application verifies -another size with its workload. With Effect 4.0.0, an overflowing buffer of size -one can stall other calls; larger chunks and unfinished queue offers also need -application checks. A client chunk Schema failure can fail unrelated calls, and -larger buffers do not isolate it. Keep streaming schemas compatible. +Unless the application verifies another size with its workload, keep the native +WebSocket buffer default of 16. With Effect 4.0.0, an overflowing buffer of size +one can stall other calls. The application also needs to check larger chunks and +unfinished queue offers. A client chunk Schema failure can fail unrelated calls. +Larger buffers do not isolate that failure. Keep streaming schemas compatible. Single-element arrays in GET and form-urlencoded payloads can fail native server decoding. Array query parameters and JSON array responses retain their representation. -The factories preserve the ready client's actual execution contract; they supply +The factories preserve the ready client's actual execution contract. They supply no transport batching, platform services, or framework providers. Error guards use -same-realm `instanceof`; transfer safe error DTOs deliberately across realms. +same-realm `instanceof`. Across realms, explicitly transfer safe error DTOs. Sources: [Vue Query](https://ueberbrot.github.io/effect-api-query/guides/vue-query/), [Solid Query](https://ueberbrot.github.io/effect-api-query/guides/solid-query/), diff --git a/skills/effect-api-query/references/http.md b/skills/effect-api-query/references/http.md index 423319e..fe386bb 100644 --- a/skills/effect-api-query/references/http.md +++ b/skills/effect-api-query/references/http.md @@ -1,8 +1,8 @@ # HTTP setup and rules Use the application's existing HttpApi declaration and ready HttpApiClient. This -standalone example expects a server at `http://localhost:3000` implementing the -same declaration. +standalone example requires a server at `http://localhost:3000` that implements +the same declaration. ```ts import { MutationObserver, QueryClient } from '@tanstack/query-core' @@ -86,66 +86,68 @@ try { ## Tree and request semantics -Ordinary groups produce `http[groupId][endpointId]`; top-level groups put their -endpoints at the root. Dots in HTTP identifiers remain literal properties. This -differs from dotted RPC tag projection. +Ordinary groups produce `http[groupId][endpointId]`. Top-level groups put their +endpoints at the root. Dots in HTTP identifiers remain literal properties, unlike +the nested paths produced by dotted RPC tags. -Provide the declared `params`, `query`, `headers`, and ordinary `payload` containers in -their decoded types. Buffered multipart payloads use explicit `FormData`. For a -`Schema.FiniteFromString` field, pass a number; the client performs wire encoding. -HTTP inputs receive no RPC constructor defaults. -A declared container remains required even if all fields inside it are optional: -use `{ query: {} }` for an empty declared query container. +Provide the declared `params`, `query`, `headers`, and ordinary `payload` containers +with their decoded types. Buffered multipart payloads use explicit `FormData`. +For a `Schema.FiniteFromString` field, pass a number. The client performs wire +encoding. HTTP inputs receive no RPC constructor defaults. A declared container +is required even when all its fields are optional. For an empty declared query +container, use `{ query: {} }`. -Mutation variables use that same complete request shape. The adapter forces -decoded-only responses; raw-response controls are not part of the input. Text -responses, binary responses, and declared response-header wrappers keep their decoded types. +Mutation variables use the same complete request shape. The adapter forces +decoded-only responses. The input excludes raw-response controls. Text responses, +binary responses, and declared response-header wrappers retain their decoded types. Buffered endpoints without multipart expose query, infinite-query, and mutation -builders. Any buffered multipart payload alternative makes the whole endpoint -mutation-only, including mixed plain and multipart alternatives. Those leaves expose -only `key()`, `mutationKey()`, and `mutationOptions()`, regardless of HTTP method. -Mixed alternatives preserve Effect's client request union. - -An endpoint with exactly one SSE success exposes `streamedKey` / `streamedOptions` for -ordered history and `liveKey` / `liveOptions` for the latest decoded value. Declared -response-header wrappers surround each emission. Keep `sseOptions` beside decoded -`input`; live keys include decoder policy, while accumulated keys also include retention -and refetch policy. Applications own reconnect and resume controls. Empty live completion -raises `EffectHttpApiQueryEmptyStreamError`; a top-level `undefined` emission becomes `null`. - -Raw byte streams, mixed buffered/SSE successes, multipart SSE payloads, and streaming -multipart request alternatives omit the whole endpoint. Empty groups disappear. Consume -unsupported streams through the underlying Effect client. +builders. Any buffered multipart payload alternative makes the entire endpoint +mutation-only. This includes mixed plain and multipart alternatives. Those leaves +expose only `key()`, `mutationKey()`, and `mutationOptions()`, regardless of HTTP +method. Mixed alternatives preserve Effect's client request union. + +An endpoint with exactly one SSE success exposes `streamedKey` and `streamedOptions` +for ordered history. It exposes `liveKey` and `liveOptions` for the latest decoded +value. Declared response-header wrappers surround each emission. Keep `sseOptions` +beside decoded `input`. Live keys include decoder policy. Accumulated keys also +include retention and refetch policy. Applications own reconnect and resume +controls. Empty live completion raises `EffectHttpApiQueryEmptyStreamError`. +A top-level `undefined` emission becomes `null`. + +The adapter omits endpoints with raw byte streams, mixed buffered/SSE successes, +multipart SSE payloads, or streaming multipart request alternatives. Empty groups +disappear. To consume unsupported streams, use the underlying Effect client. ## Read buffered metadata -Use `metadataOptions` when a read needs decoded data, response status, or raw -string headers. Use `metadataKey(input)` for typed cache access. Its `metadata` -discriminator separates it from ordinary data; a leaf prefix matches both views. -Metadata has no mutation or infinite builders and is absent on multipart and -streaming endpoints. +If a read needs decoded data, response status, or raw string headers, use +`metadataOptions`. For typed cache access, use `metadataKey(input)`. Its `metadata` +discriminator separates metadata from ordinary data. A leaf prefix matches both +views. Metadata has no mutation or infinite builders. Multipart and streaming +endpoints have no metadata view. -Fetched envelopes and their copied header records are frozen. The decoded `data` -remains mutable and retains any declared decoded header wrapper. Only an entirely -undefined success becomes `null`. Initial data, hydration, manual writes, and -selected results remain application-owned. +The adapter freezes fetched envelopes and their copied header records. Decoded +`data` stays mutable and retains any declared decoded header wrapper. Only an +entirely undefined success becomes `null`. The application owns initial data, +hydration, manual writes, and selected results. Native `select`, skipping, defaults, and cancellation apply. Global and prefix `structuralSharing` policies apply to fetched metadata snapshots. Selected results -and later manual writes use an explicit policy passed to `metadataOptions`, or -standard deep sharing when omitted. An inherited sharing callback does not govern -those later values. A fresh-cache hit from `queryClient.query` does not replace an -existing Query's options. Install a changed policy through an observer or an actual -fetch before relying on it for manual writes. +and later manual writes use the explicit policy passed to `metadataOptions`. +If no policy is passed, they use standard deep sharing. An inherited sharing +callback does not control those later values. A fresh-cache hit from +`queryClient.query` does not replace an existing Query's options. Before relying +on a changed policy for manual writes, install it through an observer or an actual fetch. -Apply an application disclosure policy before persisting raw headers: their strings -receive no inspection redaction. +Before persisting raw headers, apply the application's disclosure policy. +Header strings receive no inspection redaction. ## Consume decoded SSE -Keep decoder options beside the request. This function accepts an already acquired -client for the same declaration and returns independent history and latest views: +Keep decoder options beside the request. The following function accepts an already +acquired client for the same declaration. It returns independent history and +latest views: ```ts import { Schema } from 'effect' @@ -179,43 +181,45 @@ export function httpEventOptions(client: HttpApiClient.ForApi) ``` The accumulated key includes normalized `maxChunks`, `refetchMode`, and -`sseOptions.maxEventSize`; the live key includes the decoder limit. Its default is -10 MiB per SSE event, distinct from the cache's element bound. Applications own -reconnect, replay, and resume. For refetch visibility and retention, read -[query patterns](query-patterns.md); for request capture, read +`sseOptions.maxEventSize`. The live key includes the decoder limit. That limit +defaults to 10 MiB per SSE event, separately from the cache's element bound. +Applications own reconnect, replay, and resume. For refetch visibility and +retention, read [query patterns](query-patterns.md). For request capture, read [hydration and SSR](hydration-and-ssr.md). ## Multipart mutations The example declares `users.upload` with a buffered multipart file payload and -numeric `params.id`. Build the multipart fields and files explicitly in `FormData`, -then pass it as `payload` alongside the decoded request containers when calling `mutate`. +numeric `params.id`. Build the multipart fields and files explicitly in `FormData`. +When calling `mutate`, pass that value as `payload` alongside the decoded request +containers. -The adapter forwards the original `FormData` to the ready client. The decoded success, -callback variables, execution services, and error Cause follow the ordinary mutation -contract. Keep multipart mutation-only endpoints out of `keyEncoders`; encoder entries -are rejected because mutation variables and files never enter query identity. +The adapter forwards the original `FormData` to the ready client. Decoded success, +callback variables, execution services, and the error Cause follow the ordinary +mutation contract. Keep multipart mutation-only endpoints out of `keyEncoders`. +The adapter rejects encoder entries because mutation variables and files never +enter query identity. ## Key encoding -The adapter synchronously schema-encodes labelled request parts for default keys. It omits -encoded undefined object members and retains array order and encoded null. -Header names become lowercase; conflicting duplicate header names fail. These -rules apply to default encoding, not custom encoder output. - -Configure custom encoders under declaration group and endpoint identifiers, -including top-level groups: `keyEncoders: { groupId: { endpointId: encoder } }`. -The encoder receives the complete decoded request. For endpoints with query support, -provide one when encoding requires services, uses opaque middleware, contains explicit -redacted values, or declares multiple payload alternatives. Runtime middleware inspection -is conservative even for synchronous service-free encoding. Binary query inputs need a JSON-safe projection, -such as an array of bytes. - -Return strict synchronous `JsonValue` and preserve body/content-type distinctions -between alternatives, even if two schemas encode to the same scalar. An encoder -supplies identity only; the client still performs request encoding, and the +For default keys, the adapter synchronously schema-encodes labeled request parts. +It omits encoded undefined object members and retains array order and encoded null. +Header names become lowercase. Conflicting duplicate header names fail. These +rules govern default encoding. They do not govern custom encoder output. + +Configure custom encoders under declaration group and endpoint identifiers: +`keyEncoders: { groupId: { endpointId: encoder } }`. Use that structure for top-level +groups too. The encoder receives the complete decoded request. For endpoints with +query support, provide an encoder if encoding requires services, uses opaque +middleware, contains explicit redacted values, or declares multiple payload alternatives. +Runtime middleware inspection is conservative even for synchronous service-free +encoding. Binary query inputs need a JSON-safe projection, such as an array of bytes. + +Return strict synchronous `JsonValue`. Preserve differences in body and content +type between alternatives, even if two schemas encode to the same scalar. +An encoder supplies only identity. The client still encodes requests, and the runner must provide required services. Keep ordinary authentication in client -middleware and use safe identity partitions in the key prefix. +middleware. Use safe identity partitions in the key prefix. Sources: [HTTP factory](https://ueberbrot.github.io/effect-api-query/reference/http-factory/), [HTTP builders](https://ueberbrot.github.io/effect-api-query/reference/http-builders/). diff --git a/skills/effect-api-query/references/hydration-and-ssr.md b/skills/effect-api-query/references/hydration-and-ssr.md index 6691bca..140fb53 100644 --- a/skills/effect-api-query/references/hydration-and-ssr.md +++ b/skills/effect-api-query/references/hydration-and-ssr.md @@ -2,10 +2,11 @@ ## Capture an open stream -Use a fresh request-owned QueryClient and keep its ready clients, runner, and Scope +Use a fresh request-owned QueryClient. Keep its ready clients, runner, and Scope alive until every capture and preparation settles. `queryClient.query` waits for -stream completion. Use `fetchStreamSnapshot` for the first new cache publication or -normal completion; it cancels that exact query and awaits local iterator cleanup. +stream completion. To capture the first new cache publication or normal completion, +use `fetchStreamSnapshot`. It cancels that exact query and awaits local iterator +cleanup. ```ts import { dehydrate } from '@tanstack/query-core' @@ -39,34 +40,37 @@ export async function captureUpdates( } ``` -The same helper accepts RPC or HTTP accumulated/live options. Default `fresh` mode +The helper accepts RPC or HTTP accumulated and live options. Default `fresh` mode starts a new attempt even with initial or cached success. `append` retains existing -history. A `replace` refetch of previously fetched data publishes only at completion, -so an indefinite replacement needs completion or timeout. Explicit `mode: 'cached'` -reuses successful data without starting or cancelling work, and falls back to fresh -when no successful entry exists. Native `select` and QueryClient defaults apply. - -Reserve the exact key exclusively during fresh capture: it must be idle and -unobserved. Avoid same-key writes, observers, and concurrent fetches until settlement; -an external manual publication can otherwise look like a stream emission. Other -keys keep running. Hash through QueryClient defaults; per-call `queryHash` and -`queryKeyHashFn` are rejected. - -Abort and timeout stop the data wait, then cleanup is awaited before resolution or -rejection. `timeoutMs` does not bound finalization. Local settlement does not await a -remote cancellation acknowledgement. Dispose client resources after helper settlement. +history. A `replace` refetch of previously fetched data publishes only at completion. +An indefinite replacement therefore needs completion or timeout. Explicit +`mode: 'cached'` reuses successful data without starting or canceling work. If no +successful entry exists, it falls back to fresh. Native `select` and QueryClient +defaults apply. + +During fresh capture, reserve the exact key exclusively. The query must be idle and +unobserved. Until settlement, avoid same-key writes, observers, and concurrent +fetches. An external manual publication can otherwise appear to be a stream +emission. Other keys keep running. Configure hashing through QueryClient defaults. +The helper rejects per-call `queryHash` and `queryKeyHashFn`. + +Abort and timeout stop the wait for data. The helper then awaits cleanup before +resolving or rejecting. `timeoutMs` does not bound finalization. Local settlement +does not await acknowledgment of remote cancellation. After the helper settles, +dispose client resources. ## Choose a consistent cache representation -Server and browser need equivalent contracts, inputs, safe prefixes, and hashing. -Use independent QueryClients, clients, runners, and Scopes. Choose `staleTime` to -control the first browser refetch; a new browser stream reconnects independently. +The server and browser need equivalent contracts, inputs, safe prefixes, and hashing. +Use independent QueryClients, clients, runners, and Scopes. To control the first +browser refetch, choose `staleTime`. A new browser stream reconnects independently. -Use JSON-safe success DTOs in both caches, or encode decoded domain values at the -snapshot boundary and decode them during hydration. `select` changes observer data, -not the cache. `structuredClone` does not restore class prototypes or supply JSON -codecs for bigint and bytes. This synchronous paired codec keeps rich values through -hydration and later contract refetch: +Use JSON-safe success DTOs in both caches. Alternatively, encode decoded domain +values at the snapshot boundary and decode them during hydration. `select` changes +observer data while the cache stays unchanged. `structuredClone` neither restores +class prototypes nor supplies JSON codecs for bigint and bytes. The following +synchronous paired codec preserves rich values through hydration and later contract +refetch: ```ts import { defaultShouldDehydrateQuery, dehydrate, hydrate } from '@tanstack/query-core' @@ -99,47 +103,51 @@ export const hydrateProfile = (queryClient: QueryClient, json: string): void => }) ``` -Reserve this recipe's prefix for unary `Profile` data and declare `Profile` as the -operation success on both sides. The enclosing snapshot must come from the matching -application encoder. Synchronous callbacks require service-free synchronous codecs. +Reserve this recipe's prefix for unary `Profile` data. On both sides, declare +`Profile` as the operation success. The enclosing snapshot must come from the +matching application encoder. Synchronous callbacks require service-free synchronous +codecs. For effectful or serviceful codecs, run application-owned preparation before native -dehydration transport or hydration. Await all selected encodings before publishing -one snapshot. Decode all selected entries before one native `hydrate` call, so one -failed decode leaves the destination untouched. Never store preparation Effects or -Promises as query data. Supply codec services through the owning runtime. +dehydration transport or hydration. Before publishing one snapshot, await all +selected encodings. Before one native `hydrate` call, decode all selected entries. +A failed decode then leaves the destination untouched. Keep preparation Effects +and Promises out of query data. Supply codec services through the owning runtime. Choose codecs for the complete view: -| View | Preserve | -| --------------------------- | ----------------------------------------------------------------------------- | -| Infinite data | Every `pages` value and `pageParams` representation | -| Accumulated stream | Ordered decoded values, including distinct `undefined` and `null` | -| Live stream | Latest decoded value, with top-level `undefined` already normalized to `null` | -| HTTP decoded header wrapper | Body and decoded headers, reconstructing the wrapper | -| HTTP metadata | Decoded data/wrapper, status, and approved raw string headers | - -JSON turns undefined array elements into null; use a tagged codec when accumulated -history distinguishes them. A header wrapper with an undefined body remains a -wrapper, so preserve that body separately. Encode all declared success alternatives -and rich page parameters. The package supplies no codec registry or serializer. - -Retain `defaultShouldDehydrateQuery` to omit failed queries. Custom error transfer -must separately encode `state.error` and `state.fetchFailureReason`; data codecs do +| View | Preserve | +| --------------------------- | ------------------------------------------------------------------------------- | +| Infinite data | Every `pages` value and `pageParams` representation | +| Accumulated stream | Ordered decoded values, including distinct `undefined` and `null` | +| Live stream | Latest decoded value, with top-level `undefined` already normalized to `null` | +| HTTP decoded header wrapper | Body and decoded headers, reconstructing the wrapper | +| HTTP metadata | Decoded data with any declared wrapper, status, and approved raw string headers | + +JSON turns undefined array elements into null. If accumulated history distinguishes +them, use a tagged codec. A header wrapper with an undefined body remains a +wrapper. Preserve that body separately. Encode all declared success alternatives +and rich page parameters. The package supplies neither a codec registry nor a +serializer. + +To omit failed queries, retain `defaultShouldDehydrateQuery`. Custom error transfer +must separately encode `state.error` and `state.fetchFailureReason`. Data codecs do not sanitize Causes, headers, or those fields. A safe error DTO does not automatically -reconstruct package error classes. Apply application disclosure rules before transport. +reconstruct package error classes. Before transport, apply the application's +disclosure rules. ## Drain request work before disposal Track query execution, accepted mutations, preparation, and snapshot captures in -the request owner. On request completion or abort, stop accepting work, abort pending preparation/captures, cancel -queries, and drain accepted mutations and local finalizers before closing clients or -runtimes. Cancelling QueryClient work alone does not drain codec preparation or -acknowledge finalization. Clear the request's private cache as part of its lifecycle. -The browser acquires independent resources after hydration. +the request owner. On request completion or abort, stop accepting work. Abort pending +preparation and captures, then cancel queries. Before closing clients or runtimes, +drain accepted mutations and local finalizers. Canceling QueryClient work alone +does not drain codec preparation or acknowledge finalization. Clear the request's +private cache as part of its lifecycle. After hydration, the browser acquires +independent resources. When adding query persistence to the Start example, compose its application-owned -persister. Replacing it bypasses tracking of query and iterator cleanup. +persister. A replacement bypasses tracking of query and iterator cleanup. Sources: [stream snapshots](https://ueberbrot.github.io/effect-api-query/guides/stream-snapshots/), [unary hydration](https://ueberbrot.github.io/effect-api-query/guides/hydrate-unary-data/), diff --git a/skills/effect-api-query/references/query-patterns.md b/skills/effect-api-query/references/query-patterns.md index e114a60..83980e7 100644 --- a/skills/effect-api-query/references/query-patterns.md +++ b/skills/effect-api-query/references/query-patterns.md @@ -2,41 +2,44 @@ ## Reuse inferred operation types -Import inference helpers from `effect-api-query` and apply them to a utility leaf: - -| Helper | Representation | -| ----------------------------------------------------- | --------------------------------------------------------------------- | -| `InferInput` | RPC payload constructor input, including omitted constructor defaults | -| `InferInput` | Decoded HTTP request containers; multipart payloads use `FormData` | -| `InferQueryData` | Cached ordinary query data, before `select` | -| `InferQueryData` | Decoded data, status, and copied raw headers | -| `InferQueryData` | Ordered decoded stream elements | -| `InferQueryData` | Latest normalized query data | -| `InferQueryError` | Error type carried by the concrete query key | -| `InferMutationData` | Successful mutation value, preserving `undefined` | -| `InferMutationError` | Buffered mutation execution error type | - -Specify `'infinite'` to infer `InfiniteData` with an unknown page-parameter type. -Use the actual options' tagged key when the mapper establishes a more precise -page-parameter type. Query and live data normalize `undefined` to `null`; -accumulated elements and mutation data keep it. `InferQueryError` accepts the -same view parameter as `InferQueryData`. Choose a view supported by that leaf; -stream leaves need an explicit `'live'` or `'streamed'` view. Branches and -unsupported default views infer `never`. These types describe the contract; -runner rejections still pass through at runtime. - -For the complete input, supported-view, and cache representation contract, read -[Type Inference](https://ueberbrot.github.io/effect-api-query/reference/type-inference/). +To infer operation types, import helpers from `effect-api-query` and apply them to +a utility leaf: + +| Helper | Representation | +| ----------------------------------------------------- | ----------------------------------------------------------------------- | +| `InferInput` | RPC payload constructor input, including omitted constructor defaults | +| `InferInput` | Decoded HTTP request containers, with `FormData` for multipart payloads | +| `InferQueryData` | Cached ordinary query data, before `select` | +| `InferQueryData` | Decoded data, status, and copied raw headers | +| `InferQueryData` | Ordered decoded stream elements | +| `InferQueryData` | Latest normalized query data | +| `InferQueryError` | Error type carried by the concrete query key | +| `InferMutationData` | Successful mutation value, preserving `undefined` | +| `InferMutationError` | Buffered mutation execution error type | + +To infer `InfiniteData` with an unknown page-parameter type, specify `'infinite'`. +If the mapper establishes a more precise page-parameter type, use the actual +options' tagged key. Query and live data normalize `undefined` to `null`. +Accumulated elements and mutation data retain it. + +`InferQueryError` accepts the same view parameter as `InferQueryData`. Choose a +view that the leaf supports. Stream leaves require an explicit `'live'` or +`'streamed'` view. Branches and unsupported default views infer `never`. +These types describe the contract. At runtime, runner rejections still pass +through unchanged. + +For the complete contract for inputs, supported views, and cache representations, +read [Type inference](https://ueberbrot.github.io/effect-api-query/reference/type-inference/). ## Conditional reads with React Query -Use `input: skipToken` when required input is unavailable. `enabled: false` still -constructs a concrete key and therefore needs valid input. Use `enabled: false` -when you have valid input and intend to execute through manual refetch. +If required input is unavailable, use `input: skipToken`. `enabled: false` still +constructs a concrete key and requires valid input. If you have valid input and +intend to execute through manual refetch, use `enabled: false`. -This example exports a hook factory for an application-owned ready RPC client. -Call the factory during application setup. Use the returned hook in components -under the application's `QueryClientProvider`. +The following example exports a hook factory for an application-owned ready RPC +client. During application setup, call the factory. Use its returned hook in +components under the application's `QueryClientProvider`. ```ts import { useQuery } from '@tanstack/react-query' @@ -64,17 +67,18 @@ export function makeUseUser(client: RpcClient.RpcClient.Flat