v0.18: Scalar, downloads de arquivo tipados, Collections conscientes de filtros
A v0.18 foca em uma modelagem de dados mais rica para valores que variam conforme o contexto da requisição, downloads tipados mais simples e uma correspondência de collections que reflete melhor os filtros reais das APIs.
Novos recursos:
- Schema Scalar - Campos de entidade dependentes de lente (por exemplo, valores específicos de um portfólio) sem jamais mutar a entidade subjacente
- Propriedade
contentdo RestEndpoint - Downloads de arquivo tipados, respostas em texto e streaming com uma única propriedade nonFilterArgumentKeysem resource() - Argumentos de ordenação/paginação não fragmentam suas Collections
Outras melhorias:
- Detecção automática de Content-Type binário - Imagens, PDFs e outras respostas binárias são tratadas automaticamente, sem nenhuma configuração (#3868)
- Os tipos de body dos extenders de Collection seguem a semântica dos métodos HTTP - Extenders PATCH (
.move,.remove) aceitam bodies parciais; um RestEndpoint independente deriva um body tipado do schema de entidade da Collection (#3910) - Exporta
CollectionOptionsde@data-client/endpointe@data-client/restpara a construção tipada de Collections (#3904)
import { Collection, Entity, RestEndpoint, Scalar } from '@data-client/rest'; export class Company extends Entity { id = ''; name = ''; price = 0; pct_equity = 0; shares = 0; } const PortfolioScalar = new Scalar({ lens: args => args[0]?.portfolio, key: 'portfolio', entity: Company, }); Company.schema = { pct_equity: PortfolioScalar, shares: PortfolioScalar, }; export const getCompanies = new RestEndpoint({ path: '/companies', searchParams: {} as { portfolio: string }, // `portfolio` is a lens, not a filter — the returned Company list is the same // regardless of lens. Dropping it from `argsKey` collapses every portfolio to // one Collection pk, so `Collection.queryKey()` finds the list on every // switch and `useSuspense` reuses it without refetching. schema: new Collection([Company], { argsKey: () => ({}) }), }); export const getPortfolioColumns = new RestEndpoint({ path: '/companies/columns', searchParams: {} as { portfolio: string }, schema: new Collection([PortfolioScalar], { argsKey: ({ portfolio }: { portfolio: string }) => ({ portfolio }), }), });
O exemplo mostra as mesmas entidades Company renderizadas através de diferentes lentes de portfólio. Os campos estáveis da Entity são reutilizados, enquanto os valores dependentes de lente são selecionados de células Scalar separadas.
- Schema.denormalize() recebe um delegate -
denormalize(input, args, unvisit)→denormalize(input, delegate). Afeta apenas implementações personalizadas de Schema. - Schema.normalize() recebe um delegate -
normalize(input, parent, key, args, visit, delegate)→normalize(input, parent, key, delegate). Afeta apenas implementações personalizadas de Schema.
Usa um agente de IA? Instale a skill de migração — ela executa o codemod, registra
argsKey para schemas dependentes de args e trata os casos que o codemod
não consegue (campos de classe, atribuição dinâmica de métodos, blocos <script> de SFC, helpers personalizados):
- Skills
- OpenSkills
- Claude Code
npx skills add reactive/data-client --skill data-client-v0.18-migration
npx openskills install reactive/data-client/.agents/skills/data-client-v0.18-migration
claude plugin marketplace add reactive/data-client
claude plugin install migrations@data-client
Caso contrário, execute o codemod diretamente — ele cobre os casos comuns:
npx jscodeshift -t https://dataclient.io/codemods/v0.18.js --extensions=ts,tsx,js,jsx src/
