Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .cursor/rules/benchmarking.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ When working on performance investigations or changes that might impact **core,

## React benchmark (`@examples/benchmark-react`)

When working on **`packages/react`** or comparing data-client to other React data libraries (TanStack Query, SWR, baseline), use **`@examples/benchmark-react`**.
When working on **`packages/react`** or comparing data-client to other React data libraries (TanStack Query, SWR), use **`@examples/benchmark-react`**.

- **Where it lives**: `examples/benchmark-react/`
- **How to run**: From repo root: `yarn build:benchmark-react`, then `yarn workspace example-benchmark-react preview &` and in another terminal `cd examples/benchmark-react && yarn bench`
- **What it measures**: Browser-based init/update duration, ref-stability counts, sorted-view (Query memoization), optional memory (heap delta), startup metrics (FCP/TBT), and React Profiler commit times. Compares data-client, TanStack Query, SWR, and a plain React baseline.
- **CI**: `.github/workflows/benchmark-react.yml` runs on changes to `packages/react/src/**`, `packages/core/src/**`, `packages/endpoint/src/schemas/**`, `packages/normalizr/src/**`, or `examples/benchmark-react/**` and reports via `rhysd/github-action-benchmark` (customSmallerIsBetter). CI runs **data-client only** (hot-path scenarios) to track regressions; competitor libraries (TanStack Query, SWR, baseline) are for local comparison only.
- **What it measures**: Browser-based init/update duration, ref-stability counts, sorted-view (Query memoization), optional memory (heap delta), startup metrics (FCP/TBT), and React Profiler commit times. Compares data-client, TanStack Query, and SWR.
- **CI**: `.github/workflows/benchmark-react.yml` runs on changes to `packages/react/src/**`, `packages/core/src/**`, `packages/endpoint/src/schemas/**`, `packages/normalizr/src/**`, or `examples/benchmark-react/**` and reports via `rhysd/github-action-benchmark` (customSmallerIsBetter). CI runs **data-client only** (hot-path scenarios) to track regressions; competitor libraries (TanStack Query, SWR) are for local comparison only.
- **Report viewer**: Open `examples/benchmark-react/bench/report-viewer.html` in a browser and paste `react-bench-output.json` to view a comparison table and charts. Toggle "React commit" and "Trace" filters. Use "Load history" for time-series.

See `@examples/benchmark-react/README.md` for methodology, adding a new library, and interpreting results.
Expand Down
16 changes: 8 additions & 8 deletions examples/benchmark-react/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# React Rendering Benchmark

Browser-based benchmark comparing `@data-client/react`, TanStack Query, SWR, and a plain React baseline on mount/update scenarios. Built with Webpack via `@anansi/webpack-config`. Results are reported to CI via `rhysd/github-action-benchmark`.
Browser-based benchmark comparing `@data-client/react`, TanStack Query, and SWR on mount/update scenarios. Built with Webpack via `@anansi/webpack-config`. Results are reported to CI via `rhysd/github-action-benchmark`.

## Comparison to Node benchmarks

Expand Down Expand Up @@ -52,13 +52,13 @@ The repo has two benchmark suites:

These are approximate values to help calibrate expectations. Exact numbers vary by machine and CPU throttling.

| Scenario | data-client | tanstack-query | swr | baseline |
|---|---|---|---|---|
| `getlist-100` | ~similar | ~similar | ~similar | ~similar |
| `update-shared-user-500-mounted` | Low (one store write propagates) | Higher (list refetch) | Higher (list refetch) | Higher (list refetch) |
| `ref-stability-issue-changed` (100 mounted) | ~1 changed | ~100 changed (list refetch) | ~100 changed (list refetch) | ~100 changed (list refetch) |
| `ref-stability-user-changed` (100 mounted) | ~5 changed | ~100 changed (list refetch) | ~100 changed (list refetch) | ~100 changed (list refetch) |
| `sorted-view-update-entity` | Fast (Query memoization skips re-sort) | Re-sorts on every issue change | Re-sorts on every issue change | Re-sorts on every issue change |
| Scenario | data-client | tanstack-query | swr |
|---|---|---|---|
| `getlist-100` | ~similar | ~similar | ~similar |
| `update-shared-user-500-mounted` | Low (one store write propagates) | Higher (list refetch) | Higher (list refetch) |
| `ref-stability-issue-changed` (100 mounted) | ~1 changed | ~100 changed (list refetch) | ~100 changed (list refetch) |
| `ref-stability-user-changed` (100 mounted) | ~5 changed | ~100 changed (list refetch) | ~100 changed (list refetch) |
| `sorted-view-update-entity` | Fast (Query memoization skips re-sort) | Re-sorts on every issue change | Re-sorts on every issue change |

## Expected variance

Expand Down
7 changes: 1 addition & 6 deletions examples/benchmark-react/bench/scenarios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,7 @@ const BASE_SCENARIOS: BaseScenario[] = [
},
];

export const LIBRARIES = [
'data-client',
'tanstack-query',
'swr',
'baseline',
] as const;
export const LIBRARIES = ['data-client', 'tanstack-query', 'swr'] as const;

export const SCENARIOS: Scenario[] = LIBRARIES.flatMap(lib =>
BASE_SCENARIOS.filter(
Expand Down
2 changes: 1 addition & 1 deletion examples/benchmark-react/bench/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* Usage:
* npx tsx bench/validate.ts # all libraries
* npx tsx bench/validate.ts --lib data-client # one library
* npx tsx bench/validate.ts --lib swr,baseline # multiple
* npx tsx bench/validate.ts --lib swr,tanstack-query # multiple
*/
import { chromium } from 'playwright';
import type { Page } from 'playwright';
Expand Down
267 changes: 0 additions & 267 deletions examples/benchmark-react/src/baseline/index.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion examples/benchmark-react/webpack.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const { makeConfig } = require('@anansi/webpack-config');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

const LIBRARIES = ['data-client', 'tanstack-query', 'swr', 'baseline'];
const LIBRARIES = ['data-client', 'tanstack-query', 'swr'];

const entries = {};
for (const lib of LIBRARIES) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,11 @@ interface CollectionInterface<S extends PolymorphicInterface = any, Args extends
* @see https://dataclient.io/rest/api/Collection#addWith
*/
addWith<P extends any[] = Args>(merge: (existing: any, incoming: any) => any, createCollectionFilter?: (...args: P) => (collectionKey: Record<string, string>) => boolean): Collection<S, P>;
/** Constructs a custom move schema for this collection
*
* @see https://dataclient.io/rest/api/Collection#moveWith
*/
moveWith<P extends any[] = Args>(merge: (existing: any, incoming: any) => any): Collection<S, P>;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated type definition changes bundled into benchmark PR

Low Severity

Several generated type definition files (endpoint.d.ts, graphql.d.ts, rest.d.ts, globals.d.ts, qs.d.ts) contain changes that add moveWith, unshift, and strictMerge — none of which are related to the PR's stated goal of removing the baseline benchmark app. These were likely regenerated by running copywebsitetypes.sh which picked up changes from a separate feature already landed in the packages. Mixing unrelated generated-file updates makes this PR harder to review and reason about.

Additional Locations (2)
Fix in Cursor Fix in Web

readonly cacheWith: object;
readonly schema: S;
readonly key: string;
Expand Down Expand Up @@ -1135,6 +1140,12 @@ declare class Values<Choices extends Schema = any> implements SchemaClass {
): undefined;
}

/** Collection merge that places incoming items at the start.
*
* @see https://dataclient.io/rest/api/Collection#moveWith
*/
declare const unshift: (existing: any, incoming: any) => any;

declare let CollectionRoot: CollectionConstructor;

/**
Expand Down Expand Up @@ -1183,8 +1194,9 @@ type schema_d_UnionResult<Choices extends EntityMap> = UnionResult<Choices>;
declare const schema_d_UnionRoot: typeof UnionRoot;
type schema_d_Values<Choices extends Schema = any> = Values<Choices>;
declare const schema_d_Values: typeof Values;
declare const schema_d_unshift: typeof unshift;
declare namespace schema_d {
export { schema_d_All as All, Array$1 as Array, schema_d_Collection as Collection, type schema_d_CollectionArrayAdder as CollectionArrayAdder, type schema_d_CollectionArrayOrValuesAdder as CollectionArrayOrValuesAdder, type schema_d_CollectionConstructor as CollectionConstructor, type schema_d_CollectionFromSchema as CollectionFromSchema, type schema_d_CollectionInterface as CollectionInterface, schema_d_CollectionRoot as CollectionRoot, type schema_d_DefaultArgs as DefaultArgs, EntityMixin as Entity, type schema_d_EntityInterface as EntityInterface, type schema_d_EntityMap as EntityMap, schema_d_EntityMixin as EntityMixin, schema_d_Invalidate as Invalidate, type schema_d_MergeFunction as MergeFunction, Object$1 as Object, schema_d_Query as Query, type schema_d_SchemaAttributeFunction as SchemaAttributeFunction, type schema_d_SchemaClass as SchemaClass, type schema_d_SchemaFunction as SchemaFunction, type schema_d_StrategyFunction as StrategyFunction, schema_d_Union as Union, type schema_d_UnionConstructor as UnionConstructor, type schema_d_UnionInstance as UnionInstance, type schema_d_UnionResult as UnionResult, schema_d_UnionRoot as UnionRoot, schema_d_Values as Values };
export { schema_d_All as All, Array$1 as Array, schema_d_Collection as Collection, type schema_d_CollectionArrayAdder as CollectionArrayAdder, type schema_d_CollectionArrayOrValuesAdder as CollectionArrayOrValuesAdder, type schema_d_CollectionConstructor as CollectionConstructor, type schema_d_CollectionFromSchema as CollectionFromSchema, type schema_d_CollectionInterface as CollectionInterface, schema_d_CollectionRoot as CollectionRoot, type schema_d_DefaultArgs as DefaultArgs, EntityMixin as Entity, type schema_d_EntityInterface as EntityInterface, type schema_d_EntityMap as EntityMap, schema_d_EntityMixin as EntityMixin, schema_d_Invalidate as Invalidate, type schema_d_MergeFunction as MergeFunction, Object$1 as Object, schema_d_Query as Query, type schema_d_SchemaAttributeFunction as SchemaAttributeFunction, type schema_d_SchemaClass as SchemaClass, type schema_d_SchemaFunction as SchemaFunction, type schema_d_StrategyFunction as StrategyFunction, schema_d_Union as Union, type schema_d_UnionConstructor as UnionConstructor, type schema_d_UnionInstance as UnionInstance, type schema_d_UnionResult as UnionResult, schema_d_UnionRoot as UnionRoot, schema_d_Values as Values, schema_d_unshift as unshift };
}

declare const Entity_base: IEntityClass<abstract new (...args: any[]) => {
Expand Down Expand Up @@ -1235,4 +1247,4 @@ declare function validateRequired(processedEntity: any, requiredDefaults: Record
/** https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-4.html#the-noinfer-utility-type */
type NI<T> = NoInfer<T>;

export { type AbstractInstanceType, All, Array$1 as Array, type CheckLoop, Collection, type DefaultArgs, type Denormalize, type DenormalizeNullable, type DenormalizeNullableObject, type DenormalizeObject, Endpoint, type EndpointExtendOptions, type EndpointExtraOptions, type EndpointInstance, type EndpointInstanceInterface, type EndpointInterface, type EndpointOptions, type EndpointParam, type EndpointToFunction, type EntitiesInterface, type EntitiesPath, Entity, type EntityFields, type EntityInterface, type EntityMap, EntityMixin, type EntityPath, type EntityTable, type ErrorTypes, type ExpiryStatusInterface, ExtendableEndpoint, type FetchFunction, type GetEntity, type GetIndex, type IEntityClass, type IEntityInstance, type INormalizeDelegate, type IQueryDelegate, type IndexPath, Invalidate, type KeyofEndpointInstance, type Mergeable, type MutateEndpoint, type NI, type NetworkError, type Normalize, type NormalizeNullable, type NormalizeObject, type NormalizedEntity, type NormalizedIndex, type NormalizedNullableObject, Object$1 as Object, type ObjectArgs, type PolymorphicInterface, Query, type Queryable, type ReadEndpoint, type RecordClass, type ResolveType, type Schema, type SchemaArgs, type SchemaClass, type SchemaSimple, type Serializable, type SnapshotInterface, Union, type UnknownError, Values, type Visit, schema_d as schema, validateRequired };
export { type AbstractInstanceType, All, Array$1 as Array, type CheckLoop, Collection, type DefaultArgs, type Denormalize, type DenormalizeNullable, type DenormalizeNullableObject, type DenormalizeObject, Endpoint, type EndpointExtendOptions, type EndpointExtraOptions, type EndpointInstance, type EndpointInstanceInterface, type EndpointInterface, type EndpointOptions, type EndpointParam, type EndpointToFunction, type EntitiesInterface, type EntitiesPath, Entity, type EntityFields, type EntityInterface, type EntityMap, EntityMixin, type EntityPath, type EntityTable, type ErrorTypes, type ExpiryStatusInterface, ExtendableEndpoint, type FetchFunction, type GetEntity, type GetIndex, type IEntityClass, type IEntityInstance, type INormalizeDelegate, type IQueryDelegate, type IndexPath, Invalidate, type KeyofEndpointInstance, type Mergeable, type MutateEndpoint, type NI, type NetworkError, type Normalize, type NormalizeNullable, type NormalizeObject, type NormalizedEntity, type NormalizedIndex, type NormalizedNullableObject, Object$1 as Object, type ObjectArgs, type PolymorphicInterface, Query, type Queryable, type ReadEndpoint, type RecordClass, type ResolveType, type Schema, type SchemaArgs, type SchemaClass, type SchemaSimple, type Serializable, type SnapshotInterface, Union, type UnknownError, Values, type Visit, schema_d as schema, unshift, validateRequired };
Loading
Loading