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
26 changes: 26 additions & 0 deletions .changeset/modern-grapes-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
"@data-client/core": patch
"@data-client/endpoint": patch
"@data-client/graphql": patch
"@data-client/react": patch
"@data-client/rest": patch
"@data-client/vue": patch
---

Add direct exports for schema classes from `@data-client/endpoint`

Schema classes (`Union`, `Invalidate`, `Collection`, `Query`, `Values`, `All`) can now be imported directly instead of requiring the `schema` namespace.

#### Before
```ts
import { schema } from '@data-client/endpoint';
const myUnion = new schema.Union({ users: User, groups: Group }, 'type');
```

#### After
```ts
import { Union } from '@data-client/endpoint';
const myUnion = new Union({ users: User, groups: Group }, 'type');
```

The `schema` namespace export remains available for backward compatibility.
4 changes: 3 additions & 1 deletion .cursor/commands/changeset.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ Generate changesets, update documentation, draft blog entries, and prepare PR de
- Summarize motivation from changeset descriptions
- Describe solution at a high level first (not implementation details)
- Include any mermaid diagrams that might help convey key concepts, especially if one was present in a plan.md
- Drop 'Open questions' section if no relevant content
- Keep in mind you are a chat agent talking in markdown, so cannot start a markdown block without escaping the contents.

## Changeset Format
- **First line**: Action verb ("Add", "Fix", "Update", "Remove")
Expand All @@ -55,4 +57,4 @@ Generate changesets, update documentation, draft blog entries, and prepare PR de
- Multiple use cases: Separate with brief labels

## Markdown Formatting
Follow `@.cursor/rules/markdown-formatting.mdc` for all markdown content.
Follow `@.cursor/rules/markdown-formatting.mdc` for all markdown content including the PR desc.
1 change: 1 addition & 0 deletions .cursor/rules/markdown-formatting.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Link API concepts that have corresponding doc pages:

- **External doc links**: `[Union](https://dataclient.io/rest/api/Union)`
- **Internal doc links**: Use site paths like `[Controller](/docs/api/Controller)` or `[Entity](/rest/api/Entity)`
- **Package links**: `[@data-client/rest](www.npmjs.com/package/@data-client/rest)`

## Repository Links

Expand Down
4 changes: 2 additions & 2 deletions .cursor/rules/react.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ ctrl.fetch(), ctrl.fetchIfStale(), ctrl.expireAll(), ctrl.invalidate(), ctrl.inv
## Programmatic queries

```ts
const queryRemainingTodos = new schema.Query(
const queryRemainingTodos = new Query(
TodoResource.getList.schema,
entries => entries.filter(todo => !todo.completed).length,
);
Expand All @@ -93,7 +93,7 @@ const firstUserRemainingTodos = useQuery(queryRemainingTodos, { userId: 1 });
```

```ts
const groupTodoByUser = new schema.Query(
const groupTodoByUser = new Query(
TodoResource.getList.schema,
todos => Object.groupBy(todos, todo => todo.userId),
);
Expand Down
22 changes: 11 additions & 11 deletions .cursor/rules/schema.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,33 @@ to represent the data expected.
### Object

- [Entity](https://dataclient.io/rest/api/Entity) - represents a single unique object (denormalized)
- [new schema.Union(Entity)](https://dataclient.io/rest/api/Union) - polymorphic objects (A | B)
- [new Union(Entity)](https://dataclient.io/rest/api/Union) - polymorphic objects (A | B)
- `{[key:string]: Schema}` - immutable objects
- `new schema.Invalidate(Entity)` - to delete an Entity
- `new Invalidate(Entity)` - to delete an Entity

### List

- [new schema.Collection([Entity])](https://dataclient.io/rest/api/Collection) - mutable/growable lists
- [new Collection([Entity])](https://dataclient.io/rest/api/Collection) - mutable/growable lists
- `[Entity]` - immutable lists
- `new schema.All(Entity)` - list all Entities of a kind
- `new All(Entity)` - list all Entities of a kind

### Map

- `new schema.Collection(schema.Values(Entity))` - mutable/growable maps
- `new schema.Values(Entity)` - immutable maps
- `new Collection(Values(Entity))` - mutable/growable maps
- `new Values(Entity)` - immutable maps

### Programmatic

- [new schema.Query(Queryable)](https://dataclient.io/rest/api/Query) - memoized programmatic selectors
- [new Query(Queryable)](https://dataclient.io/rest/api/Query) - memoized programmatic selectors
```ts
const queryRemainingTodos = new schema.Query(
const queryRemainingTodos = new Query(
TodoResource.getList.schema,
entries => entries.filter(todo => !todo.completed).length,
);
```

```ts
const groupTodoByUser = new schema.Query(
const groupTodoByUser = new Query(
TodoResource.getList.schema,
todos => Object.groupBy(todos, todo => todo.userId),
);
Expand All @@ -58,7 +58,7 @@ to represent the data expected.

## 3. **Union Types (Polymorphic Schemas)**

To define polymorphic resources (e.g., events), use [schema.Union](https://dataclient.io/rest/api/Union) and a discriminator field.
To define polymorphic resources (e.g., events), use [Union](https://dataclient.io/rest/api/Union) and a discriminator field.

```typescript
import { schema } from '@data-client/rest';
Expand All @@ -72,7 +72,7 @@ export class IssuesEvent extends Event { /* ... */ }

export const EventResource = resource({
path: '/users/:login/events/public/:id',
schema: new schema.Union(
schema: new Union(
{
PullRequestEvent,
IssuesEvent,
Expand Down
2 changes: 1 addition & 1 deletion .cursor/rules/vue-testing.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ expect(result.current?.value).toBe(undefined);

**Testing nested collections:**
```typescript
const userTodos = new schema.Collection(new schema.Array(Todo), {
const userTodos = new Collection(new schema.Array(Todo), {
argsKey: ({ userId }) => ({ userId }),
});

Expand Down
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
- Examples: `examples/nextjs/resources/TodoResource.ts`, `examples/github-app/src/resources/Issue.tsx`.
- Place app-level API definitions and custom Managers in `src/resources/` within examples/apps (e.g., `examples/todo-app/src/resources/`).
- Managers communicate via actions and the `Controller`; import `actionTypes` from `@data-client/react` for type checks.
- Programmatic queries use `new schema.Query(...)` with a Resource’s schema; see `README.md` examples.
- Programmatic queries use `new Query(...)` with a Resource’s schema; see `README.md` examples.

### Integration details
- Babel (`babel.config.js`) resolves relative `.js` imports to `.ts` in tests; when `COMPILE_TARGET=native`, it prefers `.native.*` files.
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ ctrl.set(Article, { id }, articleData);
### [Programmatic queries](https://dataclient.io/rest/api/Query)

```typescript
const queryTotalVotes = new schema.Query(
new schema.Collection([BlogPost]),
const queryTotalVotes = new Query(
new Collection([BlogPost]),
posts => posts.reduce((total, post) => total + post.votes, 0),
);

Expand All @@ -150,7 +150,7 @@ const totalVotesForUser = useQuery(queryTotalVotes, { userId });
```

```typescript
const groupTodoByUser = new schema.Query(
const groupTodoByUser = new Query(
TodoResource.getList.schema,
todos => Object.groupBy(todos, todo => todo.userId),
);
Expand Down
4 changes: 2 additions & 2 deletions __tests__/UnionSchema.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Entity, schema } from '@data-client/rest';
import { Entity, Union } from '@data-client/rest';

export enum SequenceType {
TemporalCatDiff = 'temporal_cat_diff',
Expand Down Expand Up @@ -30,7 +30,7 @@ export class ContinuousSequence extends Sequence {
export const waterfallSchema = {
analysisId: '',
sequences: [
new schema.Union(
new Union(
{
[SequenceType.TemporalCatCount]: CategoricalSequence,
[SequenceType.TemporalCatDiff]: CategoricalSequence,
Expand Down
9 changes: 5 additions & 4 deletions __tests__/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Temporal } from '@js-temporal/polyfill';
import React, { createContext, useContext } from 'react';

import {
schema,
Endpoint,
resource,
RestEndpoint,
Expand All @@ -15,6 +14,8 @@ import {
RestInstance,
Resource,
ResourceOptions,
Union,
Invalidate,
} from '@data-client/rest';

/** Represents data with primary key being from 'id' field. */
Expand Down Expand Up @@ -446,7 +447,7 @@ export const FutureArticleResource = {
CoolerArticleResource.delete as any as RestType<
string | number,
undefined,
schema.Invalidate<typeof CoolerArticle>,
Invalidate<typeof CoolerArticle>,
true
>
).extend({
Expand Down Expand Up @@ -611,7 +612,7 @@ export class SecondUnion extends UnionBase {
readonly secondeOnlyField: number = 10;
}

export const UnionSchema = new schema.Union(
export const UnionSchema = new Union(
{
first: FirstUnion,
second: SecondUnion,
Expand All @@ -627,7 +628,7 @@ export const UnionResource = {
// just to test the other type of union def
getList: UnionResourceBase.getList.extend({
schema: [
new schema.Union(
new Union(
{
first: FirstUnion,
second: SecondUnion,
Expand Down
6 changes: 3 additions & 3 deletions docs/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,19 +290,19 @@ function ArticleEdit() {
### More data modeling

What if our entity is not the top level item? Here we define the `getList`
endpoint with [new schema.Collection([Todo])](/rest/api/Collection) as its schema. [Schemas](./concepts/normalization.md#schema) tell Reactive Data Client _where_ to find
endpoint with [new Collection([Todo])](/rest/api/Collection) as its schema. [Schemas](./concepts/normalization.md#schema) tell Reactive Data Client _where_ to find
the Entities. By placing inside a list, Reactive Data Client knows to expect a response
where each item of the list is the entity specified.

```typescript {6}
import { RestEndpoint, schema } from '@data-client/rest';
import { RestEndpoint, Collection } from '@data-client/rest';

// get and update definitions omitted

const getList = new RestEndpoint({
urlPrefix: 'https://jsonplaceholder.typicode.com',
path: '/todos',
schema: new schema.Collection([Todo]),
schema: new Collection([Todo]),
searchParams: {} as { userId?: string | number } | undefined,
paginationField: 'page',
});
Expand Down
2 changes: 1 addition & 1 deletion docs/core/api/useDLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ export const getPosts = new RestEndpoint({
path: '/post',
searchParams: { page: '' },
schema: {
results: new schema.Collection([PaginatedPost]),
results: new Collection([PaginatedPost]),
nextPage: '',
lastPage: '',
},
Expand Down
4 changes: 2 additions & 2 deletions docs/core/api/useDebounce.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ When loading new data, the [AsyncBoundary](./AsyncBoundary.md) will continue ren
<HooksPlayground row>

```ts title="IssueQuery" collapsed
import { RestEndpoint, Entity, schema } from '@data-client/rest';
import { RestEndpoint, Entity, Collection } from '@data-client/rest';

export class Issue extends Entity {
number = 0;
Expand Down Expand Up @@ -62,7 +62,7 @@ export const issueQuery = new RestEndpoint({
paginationField: 'page',
schema: {
incomplete_results: false,
items: new schema.Collection([Issue]),
items: new Collection([Issue]),
total_count: 0,
},
});
Expand Down
6 changes: 3 additions & 3 deletions docs/core/api/useQuery.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,16 @@ export const UserResource = resource({
```

```tsx title="UsersPage" {22}
import { schema } from '@data-client/rest';
import { Query } from '@data-client/rest';
import { useQuery, useFetch } from '@data-client/react';
import { UserResource, User } from './UserResource';

interface Args {
asc: boolean;
isAdmin?: boolean;
}
const sortedUsers = new schema.Query(
new schema.All(User),
const sortedUsers = new Query(
new All(User),
(entries, { asc, isAdmin }: Args = { asc: false }) => {
let sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name));
if (isAdmin !== undefined)
Expand Down
2 changes: 1 addition & 1 deletion docs/core/api/useSuspense.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ export const getPosts = new RestEndpoint({
path: '/post',
searchParams: { page: '' },
schema: {
posts: new schema.Collection([PaginatedPost]),
posts: new Collection([PaginatedPost]),
nextPage: '',
lastPage: '',
},
Expand Down
5 changes: 4 additions & 1 deletion docs/core/concepts/expiry-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,8 @@ delay: () => 150,
>

```ts title="api/lastUpdated" collapsed
import { Entity, RestEndpoint } from '@data-client/rest';

export class TimedEntity extends Entity {
id = '';
updatedAt = Temporal.Instant.fromEpochMilliseconds(0);
Expand Down Expand Up @@ -611,11 +613,12 @@ export default function TimePage({ id }) {
```

```tsx title="ShowTime"
import { Invalidate } from '@data-client/rest';
import { useLoading } from '@data-client/react';
import { TimedEntity } from './api/lastUpdated';
import TimePage from './TimePage';

const InvalidateTimedEntity = new schema.Invalidate(TimedEntity);
const InvalidateTimedEntity = new Invalidate(TimedEntity);
export const deleteLastUpdated = new RestEndpoint({
path: '/api/currentTime/:id',
method: 'DELETE',
Expand Down
12 changes: 6 additions & 6 deletions docs/core/concepts/normalization.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ values={[
```typescript
const getPresentations = new Endpoint(
() => fetch(`/presentations`).then(res => res.json()),
{ schema: new schema.Collection([Presentation]) },
{ schema: new Collection([Presentation]) },
);
```

Expand Down Expand Up @@ -137,7 +137,7 @@ const todoCreate = new RestEndpoint({
urlPrefix: 'https://jsonplaceholder.typicode.com',
path: '/todos',
method: 'POST',
schema: new schema.Collection([Todo]).push,
schema: new Collection([Todo]).push,
});
```

Expand Down Expand Up @@ -203,13 +203,13 @@ export default function UpdateTodoForm({ id }: { id: number }) {
<TabItem value="Delete">

```typescript
import { schema, RestEndpoint } from '@data-client/rest';
import { Invalidate, RestEndpoint } from '@data-client/rest';

const todoDelete = new RestEndpoint({
urlPrefix: 'https://jsonplaceholder.typicode.com',
path: '/todos/:id',
method: 'DELETE',
schema: new schema.Invalidate(Todo),
schema: new Invalidate(Todo),
});
```

Expand Down Expand Up @@ -251,13 +251,13 @@ Schemas are a declarative definition of how to [process responses](/rest/api/sch
- Functions to [deserialize fields](/rest/guides/network-transform#deserializing-fields)

```typescript
import { RestEndpoint, schema } from '@data-client/rest';
import { RestEndpoint, Collection } from '@data-client/rest';

const getTodoList = new RestEndpoint({
urlPrefix: 'https://jsonplaceholder.typicode.com',
path: '/todos',
// highlight-next-line
schema: new schema.Collection([Todo]),
schema: new Collection([Todo]),
});
```

Expand Down
Loading