Releases: reactive/data-client
@data-client/vue@0.18.1
@data-client/rest@0.18.1
📝 Read the full release announcement
Patch Changes
adb82f1- RestEndpoint.remove.name is 'partialUpdate' since it uses 'PATCH'
@data-client/react@0.18.1
@data-client/normalizr@0.18.1
@data-client/core@0.18.1
@data-client/vue@0.18.0
📝 Read the full release announcement
Minor Changes
-
#3931
959465a- Allow oneCollectionschema to be used both top-level and nested.Before:
const getTodos = new Collection([Todo], { argsKey }); const userTodos = new Collection([Todo], { nestKey });
After:
const userTodos = new Collection([Todo], { argsKey, nestKey });
-
#3887
84078d7- BREAKING:Schema.denormalize()is now(input, delegate)instead
of the previous(input, args, unvisit)3-parameter signature.// before denormalize(input, args, unvisit) { return unvisit(this.schema, input); } // after denormalize(input, delegate) { return delegate.unvisit(this.schema, input); }
The new
IDenormalizeDelegate
exposesunvisit,args, and a newargsKey(fn)helper that registers
a memoization dimension when output varies with endpoint args. Reading
delegate.argsdirectly does not contribute to cache invalidation —
schemas that branch on args must callargsKey. Thefnreference
doubles as the cache path key, so it must be referentially stable
— define it on the instance or at module scope, not inline per call:class LensSchema { constructor({ lens }) { this.lensSelector = lens; // stable reference across calls } denormalize(input, delegate) { const portfolio = delegate.argsKey(this.lensSelector); return this.lookup(input, portfolio); } }
All built-in schemas (
Array,Object,Values,Union,Query,
Invalidate,Lazy,Collection) have been updated. Custom schemas
implementingSchemaSimplemust update theirdenormalizesignature.Schema.normalize()and thevisit()callback also gain an optional
trailingparentEntityargument tracking the nearest enclosing
entity-like schema. This is additive — existing schemas don't need
changes unless they want to use it. -
#3887
84078d7- Add Scalar schema for lens-dependent entity fields.Scalarmodels entity fields whose values vary by a runtime "lens" (such as the
selected portfolio, currency, or locale). Multiple components can render the
same entity through different lenses simultaneously — each sees the correct
values without the entity itself ever being mutated. Lens-dependent values are
stored in a separate cell table and joined at denormalize time from endpoint
args.New exports:
Scalar,schema.Scalar.A single
Scalarinstance can serve both as anEntity.schemafield (parent
entity inferred from the visit) and standalone — insideValues(Scalar),
[Scalar], orCollection([Scalar])— for cheap column-only refreshes
(entity bound explicitly viaentity). Cell pks are derived from the map key
or viaScalar.entityPk(), which defaults toEntity.pk()so custom and
composite primary keys work with no override:import { Collection, Entity, RestEndpoint, Scalar } from '@data-client/rest'; class Company extends Entity { id = ''; 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, }; // Full load — Company rows + scalar cells for the current portfolio export const getCompanies = new RestEndpoint({ path: '/companies', searchParams: {} as { portfolio: string }, schema: new Collection([Company], { argsKey: () => ({}) }), }); // Lens-only refresh — writes to the same Scalar(portfolio) cell table export const getPortfolioColumns = new RestEndpoint({ path: '/companies/columns', searchParams: {} as { portfolio: string }, schema: new Collection([PortfolioScalar], { argsKey: ({ portfolio }) => ({ portfolio }), }), });
useSuspense(getCompanies, { portfolio: 'A' })and
useSuspense(getCompanies, { portfolio: 'B' })resolve to different
pct_equity/shareswhile sharing the sameCompanyrow.Scalar.queryKeyenumerates cells in its table for the current lens, so
endpoints that useScalardirectly as their top-level schema reconstruct
from cache without a network round-trip once the cells are present.
Patch Changes
@data-client/test@0.18.0
📝 Read the full release announcement
Patch Changes
89e06d3- Bump@data-client/reactpeer dependency range to include^0.18.0.
@data-client/rest@0.18.0
📝 Read the full release announcement
Minor Changes
-
#3931
959465a- Allow oneCollectionschema to be used both top-level and nested.Before:
const getTodos = new Collection([Todo], { argsKey }); const userTodos = new Collection([Todo], { nestKey });
After:
const userTodos = new Collection([Todo], { argsKey, nestKey });
-
#3887
84078d7- BREAKING:Schema.denormalize()is now(input, delegate)instead
of the previous(input, args, unvisit)3-parameter signature.// before denormalize(input, args, unvisit) { return unvisit(this.schema, input); } // after denormalize(input, delegate) { return delegate.unvisit(this.schema, input); }
The new
IDenormalizeDelegate
exposesunvisit,args, and a newargsKey(fn)helper that registers
a memoization dimension when output varies with endpoint args. Reading
delegate.argsdirectly does not contribute to cache invalidation —
schemas that branch on args must callargsKey. Thefnreference
doubles as the cache path key, so it must be referentially stable
— define it on the instance or at module scope, not inline per call:class LensSchema { constructor({ lens }) { this.lensSelector = lens; // stable reference across calls } denormalize(input, delegate) { const portfolio = delegate.argsKey(this.lensSelector); return this.lookup(input, portfolio); } }
All built-in schemas (
Array,Object,Values,Union,Query,
Invalidate,Lazy,Collection) have been updated. Custom schemas
implementingSchemaSimplemust update theirdenormalizesignature.Schema.normalize()and thevisit()callback also gain an optional
trailingparentEntityargument tracking the nearest enclosing
entity-like schema. This is additive — existing schemas don't need
changes unless they want to use it. -
#3887
84078d7- Add Scalar schema for lens-dependent entity fields.Scalarmodels entity fields whose values vary by a runtime "lens" (such as the
selected portfolio, currency, or locale). Multiple components can render the
same entity through different lenses simultaneously — each sees the correct
values without the entity itself ever being mutated. Lens-dependent values are
stored in a separate cell table and joined at denormalize time from endpoint
args.New exports:
Scalar,schema.Scalar.A single
Scalarinstance can serve both as anEntity.schemafield (parent
entity inferred from the visit) and standalone — insideValues(Scalar),
[Scalar], orCollection([Scalar])— for cheap column-only refreshes
(entity bound explicitly viaentity). Cell pks are derived from the map key
or viaScalar.entityPk(), which defaults toEntity.pk()so custom and
composite primary keys work with no override:import { Collection, Entity, RestEndpoint, Scalar } from '@data-client/rest'; class Company extends Entity { id = ''; 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, }; // Full load — Company rows + scalar cells for the current portfolio export const getCompanies = new RestEndpoint({ path: '/companies', searchParams: {} as { portfolio: string }, schema: new Collection([Company], { argsKey: () => ({}) }), }); // Lens-only refresh — writes to the same Scalar(portfolio) cell table export const getPortfolioColumns = new RestEndpoint({ path: '/companies/columns', searchParams: {} as { portfolio: string }, schema: new Collection([PortfolioScalar], { argsKey: ({ portfolio }) => ({ portfolio }), }), });
useSuspense(getCompanies, { portfolio: 'A' })and
useSuspense(getCompanies, { portfolio: 'B' })resolve to different
pct_equity/shareswhile sharing the sameCompanyrow.Scalar.queryKeyenumerates cells in its table for the current lens, so
endpoints that useScalardirectly as their top-level schema reconstruct
from cache without a network round-trip once the cells are present.
Patch Changes
-
#3925
6e8e499- Fix cached journey being mutated on repeated result-cache hits.GlobalCache.getResultscalledpaths.shift()on a cache hit, mutating
thejourneyarray stored by reference on theWeakDependencyMapLink
node. After the first hit stripped the placeholder input slot, every
subsequent hit on the same cached entry would shift off a real
EntityPath, progressively losing subscription entries. This could cause
missedcountReftracking (premature GC of still-referenced entities)
and incorrectentityExpiresAtcalculations. The hit path now returns a
non-mutating copy. -
Updated dependencies [
959465a,84078d7,6e8e499,396d163,84078d7]:- @data-client/endpoint@0.18.0
@data-client/react@0.18.0
📝 Read the full release announcement
Minor Changes
-
#3931
959465a- Allow oneCollectionschema to be used both top-level and nested.Before:
const getTodos = new Collection([Todo], { argsKey }); const userTodos = new Collection([Todo], { nestKey });
After:
const userTodos = new Collection([Todo], { argsKey, nestKey });
-
#3887
84078d7- BREAKING:Schema.denormalize()is now(input, delegate)instead
of the previous(input, args, unvisit)3-parameter signature.// before denormalize(input, args, unvisit) { return unvisit(this.schema, input); } // after denormalize(input, delegate) { return delegate.unvisit(this.schema, input); }
The new
IDenormalizeDelegate
exposesunvisit,args, and a newargsKey(fn)helper that registers
a memoization dimension when output varies with endpoint args. Reading
delegate.argsdirectly does not contribute to cache invalidation —
schemas that branch on args must callargsKey. Thefnreference
doubles as the cache path key, so it must be referentially stable
— define it on the instance or at module scope, not inline per call:class LensSchema { constructor({ lens }) { this.lensSelector = lens; // stable reference across calls } denormalize(input, delegate) { const portfolio = delegate.argsKey(this.lensSelector); return this.lookup(input, portfolio); } }
All built-in schemas (
Array,Object,Values,Union,Query,
Invalidate,Lazy,Collection) have been updated. Custom schemas
implementingSchemaSimplemust update theirdenormalizesignature.Schema.normalize()and thevisit()callback also gain an optional
trailingparentEntityargument tracking the nearest enclosing
entity-like schema. This is additive — existing schemas don't need
changes unless they want to use it. -
#3887
84078d7- Add Scalar schema for lens-dependent entity fields.Scalarmodels entity fields whose values vary by a runtime "lens" (such as the
selected portfolio, currency, or locale). Multiple components can render the
same entity through different lenses simultaneously — each sees the correct
values without the entity itself ever being mutated. Lens-dependent values are
stored in a separate cell table and joined at denormalize time from endpoint
args.New exports:
Scalar,schema.Scalar.A single
Scalarinstance can serve both as anEntity.schemafield (parent
entity inferred from the visit) and standalone — insideValues(Scalar),
[Scalar], orCollection([Scalar])— for cheap column-only refreshes
(entity bound explicitly viaentity). Cell pks are derived from the map key
or viaScalar.entityPk(), which defaults toEntity.pk()so custom and
composite primary keys work with no override:import { Collection, Entity, RestEndpoint, Scalar } from '@data-client/rest'; class Company extends Entity { id = ''; 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, }; // Full load — Company rows + scalar cells for the current portfolio export const getCompanies = new RestEndpoint({ path: '/companies', searchParams: {} as { portfolio: string }, schema: new Collection([Company], { argsKey: () => ({}) }), }); // Lens-only refresh — writes to the same Scalar(portfolio) cell table export const getPortfolioColumns = new RestEndpoint({ path: '/companies/columns', searchParams: {} as { portfolio: string }, schema: new Collection([PortfolioScalar], { argsKey: ({ portfolio }) => ({ portfolio }), }), });
useSuspense(getCompanies, { portfolio: 'A' })and
useSuspense(getCompanies, { portfolio: 'B' })resolve to different
pct_equity/shareswhile sharing the sameCompanyrow.Scalar.queryKeyenumerates cells in its table for the current lens, so
endpoints that useScalardirectly as their top-level schema reconstruct
from cache without a network round-trip once the cells are present.
Patch Changes
-
#3925
6e8e499- Fix cached journey being mutated on repeated result-cache hits.GlobalCache.getResultscalledpaths.shift()on a cache hit, mutating
thejourneyarray stored by reference on theWeakDependencyMapLink
node. After the first hit stripped the placeholder input slot, every
subsequent hit on the same cached entry would shift off a real
EntityPath, progressively losing subscription entries. This could cause
missedcountReftracking (premature GC of still-referenced entities)
and incorrectentityExpiresAtcalculations. The hit path now returns a
non-mutating copy. -
Updated dependencies [
959465a,84078d7,6e8e499,84078d7]:- @data-client/core@0.18.0
@data-client/normalizr@0.18.0
📝 Read the full release announcement
Minor Changes
-
#3931
959465a- Allow oneCollectionschema to be used both top-level and nested.Before:
const getTodos = new Collection([Todo], { argsKey }); const userTodos = new Collection([Todo], { nestKey });
After:
const userTodos = new Collection([Todo], { argsKey, nestKey });
-
#3887
84078d7- BREAKING:Schema.denormalize()is now(input, delegate)instead
of the previous(input, args, unvisit)3-parameter signature.// before denormalize(input, args, unvisit) { return unvisit(this.schema, input); } // after denormalize(input, delegate) { return delegate.unvisit(this.schema, input); }
The new
IDenormalizeDelegate
exposesunvisit,args, and a newargsKey(fn)helper that registers
a memoization dimension when output varies with endpoint args. Reading
delegate.argsdirectly does not contribute to cache invalidation —
schemas that branch on args must callargsKey. Thefnreference
doubles as the cache path key, so it must be referentially stable
— define it on the instance or at module scope, not inline per call:class LensSchema { constructor({ lens }) { this.lensSelector = lens; // stable reference across calls } denormalize(input, delegate) { const portfolio = delegate.argsKey(this.lensSelector); return this.lookup(input, portfolio); } }
All built-in schemas (
Array,Object,Values,Union,Query,
Invalidate,Lazy,Collection) have been updated. Custom schemas
implementingSchemaSimplemust update theirdenormalizesignature.Schema.normalize()and thevisit()callback also gain an optional
trailingparentEntityargument tracking the nearest enclosing
entity-like schema. This is additive — existing schemas don't need
changes unless they want to use it. -
#3934
396d163- Move normalizeargsand recursivevisitinto the existing normalize delegate passed to schemas.
CustomSchema.normalize()implementations should migrate from
normalize(input, parent, key, args, visit, delegate, parentEntity?)to
normalize(input, parent, key, delegate, parentEntity?), then read
delegate.argsand calldelegate.visit()for recursive normalization.Before:
class WrapperSchema { normalize(input, parent, key, args, visit, delegate) { const normalized = visit(this.schema, input.value, input, 'value', args); delegate.mergeEntity(this, this.pk(input, parent, key, args), normalized); return normalized; } }
After:
class WrapperSchema { normalize(input, parent, key, delegate) { const { args, visit } = delegate; const normalized = visit(this.schema, input.value, input, 'value'); delegate.mergeEntity(this, this.pk(input, parent, key, args), normalized); return normalized; } }
-
#3887
84078d7- Add Scalar schema for lens-dependent entity fields.Scalarmodels entity fields whose values vary by a runtime "lens" (such as the
selected portfolio, currency, or locale). Multiple components can render the
same entity through different lenses simultaneously — each sees the correct
values without the entity itself ever being mutated. Lens-dependent values are
stored in a separate cell table and joined at denormalize time from endpoint
args.New exports:
Scalar,schema.Scalar.A single
Scalarinstance can serve both as anEntity.schemafield (parent
entity inferred from the visit) and standalone — insideValues(Scalar),
[Scalar], orCollection([Scalar])— for cheap column-only refreshes
(entity bound explicitly viaentity). Cell pks are derived from the map key
or viaScalar.entityPk(), which defaults toEntity.pk()so custom and
composite primary keys work with no override:import { Collection, Entity, RestEndpoint, Scalar } from '@data-client/rest'; class Company extends Entity { id = ''; 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, }; // Full load — Company rows + scalar cells for the current portfolio export const getCompanies = new RestEndpoint({ path: '/companies', searchParams: {} as { portfolio: string }, schema: new Collection([Company], { argsKey: () => ({}) }), }); // Lens-only refresh — writes to the same Scalar(portfolio) cell table export const getPortfolioColumns = new RestEndpoint({ path: '/companies/columns', searchParams: {} as { portfolio: string }, schema: new Collection([PortfolioScalar], { argsKey: ({ portfolio }) => ({ portfolio }), }), });
useSuspense(getCompanies, { portfolio: 'A' })and
useSuspense(getCompanies, { portfolio: 'B' })resolve to different
pct_equity/shareswhile sharing the sameCompanyrow.Scalar.queryKeyenumerates cells in its table for the current lens, so
endpoints that useScalardirectly as their top-level schema reconstruct
from cache without a network round-trip once the cells are present.
Patch Changes
-
#3925
6e8e499- Fix cached journey being mutated on repeated result-cache hits.GlobalCache.getResultscalledpaths.shift()on a cache hit, mutating
thejourneyarray stored by reference on theWeakDependencyMapLink
node. After the first hit stripped the placeholder input slot, every
subsequent hit on the same cached entry would shift off a real
EntityPath, progressively losing subscription entries. This could cause
missedcountReftracking (premature GC of still-referenced entities)
and incorrectentityExpiresAtcalculations. The hit path now returns a
non-mutating copy.