vue-fate 1.6.2 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +279 -2
- package/lib/index.d.mts +9 -9
- package/lib/index.mjs +11 -11
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -430,6 +430,96 @@ const PostDetail = ({ post: postRef }: { post: ViewRef<'Post'> }) => {
|
|
|
430
430
|
|
|
431
431
|
ViewRefs carry a set of view names they can resolve. `useView` throws if a ref does not include the required view.
|
|
432
432
|
|
|
433
|
+
### Parameters and Conditional Selections
|
|
434
|
+
|
|
435
|
+
A view can take parameters. Define the selection with a function, then bind its parameters where you request or compose the view:
|
|
436
|
+
|
|
437
|
+
```tsx
|
|
438
|
+
import { alias, useRequest, useView, view, when, type ViewRef } from 'react-fate';
|
|
439
|
+
import type { User } from '@your-org/server/views';
|
|
440
|
+
|
|
441
|
+
const AccountView = view<User>()({ email: true });
|
|
442
|
+
|
|
443
|
+
const ProfileView = view<User>()(({ isViewer }: { isViewer: boolean }) => ({
|
|
444
|
+
name: true,
|
|
445
|
+
account: when(isViewer, alias(AccountView)),
|
|
446
|
+
}));
|
|
447
|
+
|
|
448
|
+
function Profile({ userId, isViewer }: { userId: string; isViewer: boolean }) {
|
|
449
|
+
const { user } = useRequest({
|
|
450
|
+
user: { id: userId, view: ProfileView({ isViewer }) },
|
|
451
|
+
});
|
|
452
|
+
const profile = useView(ProfileView, user);
|
|
453
|
+
const account = useView(AccountView, profile.account);
|
|
454
|
+
|
|
455
|
+
return (
|
|
456
|
+
<div>
|
|
457
|
+
<h2>{profile.name}</h2>
|
|
458
|
+
{account && <p>{account.email}</p>}
|
|
459
|
+
</div>
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
`alias(AccountView)` gives the selection a named ref to the **same User**. The name `account` belongs to your result; it does not need to exist in the server schema. Read the ref with `useView` to access the fragment's selected fields. The existing `alias('field', selection)` form still selects a schema field under another name.
|
|
465
|
+
|
|
466
|
+
`when(condition, selection)` works with named fragments and ordinary fields:
|
|
467
|
+
|
|
468
|
+
```tsx
|
|
469
|
+
const ProfileView = view<User>()(({ isViewer }: { isViewer: boolean }) => ({
|
|
470
|
+
name: true,
|
|
471
|
+
email: when(isViewer, true),
|
|
472
|
+
}));
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
A false condition returns `undefined` and excludes that branch from fetching and subscriptions, even if its data is already cached. A selected field whose server value is `null` still returns `null`. TypeScript preserves that distinction: a boolean condition adds `undefined` to the result type; literal `true` keeps the selected type; literal `false` produces `undefined`.
|
|
476
|
+
|
|
477
|
+
Both `useView` and `useLiveView` accept `undefined` refs and return `undefined` without fetching or subscribing. Call the hooks unconditionally, as in the example above.
|
|
478
|
+
|
|
479
|
+
#### Carrying Parameters with Refs
|
|
480
|
+
|
|
481
|
+
Bindings travel with refs. Child components read the view definition without reconstructing its parameters:
|
|
482
|
+
|
|
483
|
+
```tsx
|
|
484
|
+
const LocalizedNameView = view<User>()(({ locale }: { locale: string }) => ({
|
|
485
|
+
name: { args: { locale } },
|
|
486
|
+
}));
|
|
487
|
+
|
|
488
|
+
const NamesView = view<User>()({
|
|
489
|
+
english: alias(LocalizedNameView({ locale: 'en' })),
|
|
490
|
+
japanese: alias(LocalizedNameView({ locale: 'ja' })),
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
function Names({ user }: { user: ViewRef<'User'> }) {
|
|
494
|
+
const names = useView(NamesView, user);
|
|
495
|
+
const english = useView(LocalizedNameView, names.english);
|
|
496
|
+
const japanese = useView(LocalizedNameView, names.japanese);
|
|
497
|
+
|
|
498
|
+
return (
|
|
499
|
+
<p>
|
|
500
|
+
{english.name} / {japanese.name}
|
|
501
|
+
</p>
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
This example assumes the server's `name` field accepts a `locale` argument. The two refs identify the same entity with independent bindings. Connections inside named views likewise keep their own arguments and pagination state.
|
|
507
|
+
|
|
508
|
+
Declare view definitions at module scope. Binding an equivalent parameter object again preserves the logical request and ref identity; no `useMemo` is needed. Parameter objects use the same serializable values as field arguments, and fate snapshots them when binding. Keep selection functions pure, with all varying inputs passed as parameters.
|
|
509
|
+
|
|
510
|
+
Bind a parameterized definition before using it in a request, nested selection, alias, or spread. If a single ref contains multiple bindings of one definition, use named aliases to give each binding its own ref. Conditional anonymous spreads are not supported; use `when(condition, alias(View))` instead.
|
|
511
|
+
|
|
512
|
+
#### Vue
|
|
513
|
+
|
|
514
|
+
The same `view`, `alias`, and `when` API is available from `vue-fate`. Pass reactive refs or getters to the view composables:
|
|
515
|
+
|
|
516
|
+
```ts
|
|
517
|
+
const profile = useView(ProfileView, () => request.value?.user);
|
|
518
|
+
const account = useView(AccountView, () => profile.value?.account);
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
When the ref is `undefined`, the resource's value and `ready()` result are `undefined`, and the view has no active subscriptions.
|
|
522
|
+
|
|
433
523
|
## Requests
|
|
434
524
|
|
|
435
525
|
### Requesting Lists
|
|
@@ -613,6 +703,94 @@ fate.hydrate(loaderData.fate, { merge: 'replace' });
|
|
|
613
703
|
|
|
614
704
|
Do not reuse request-scoped snapshots across users. Dehydrate after awaited route preloading: snapshots are point-in-time values and do not stream cache patches for data that resolves later. Hydration and dehydration reject clients with in-flight requests, so hydrate the initial snapshot before rendering.
|
|
615
705
|
|
|
706
|
+
### Observing Request State
|
|
707
|
+
|
|
708
|
+
React's `useRequestState(request, options)` observes a request without suspending.
|
|
709
|
+
It returns `status`, `data`, `error`, and `isFetching`. Status is `pending`, `ready`,
|
|
710
|
+
or `error` for an enabled network request. An initial failure exposes the original
|
|
711
|
+
error rather than throwing during render. Request data contains the same refs as
|
|
712
|
+
`useRequest`; resolve them with `useView`.
|
|
713
|
+
|
|
714
|
+
Use `{ enabled: false }` for an empty search or an inactive screen. The status is
|
|
715
|
+
`disabled`, data is undefined, and no request starts. Changing `enabled` to true
|
|
716
|
+
starts the request. Equivalent inline request objects do not cause repeat fetches.
|
|
717
|
+
|
|
718
|
+
Use `{ mode: 'cache-only' }` to observe already-cached data. A complete selection
|
|
719
|
+
has status `ready`; an incomplete selection has status `missing` and undefined
|
|
720
|
+
data. A successful nullable root remains `ready` with a null value. Cache-only
|
|
721
|
+
requests never fetch, and their view refs cannot fetch missing fields either.
|
|
722
|
+
Mounted observers retain their request's records until unmount.
|
|
723
|
+
|
|
724
|
+
Outside React, `client.observeRequest(request, options)` exposes `getSnapshot()`
|
|
725
|
+
and `subscribe(listener)`. The first subscriber starts an enabled request;
|
|
726
|
+
unsubscribing releases its retention. Reading a snapshot alone never starts work.
|
|
727
|
+
|
|
728
|
+
Every request state includes a stable `refetch()` function. It forces a network
|
|
729
|
+
request, returns a promise for the completed result, and rejects with the original
|
|
730
|
+
error on failure. Concurrent calls share pending work. During refresh, complete
|
|
731
|
+
cached data stays available with `status: 'ready'` and `isFetching: true`. A failed
|
|
732
|
+
refresh keeps that data and exposes `error`; the next attempt clears the error.
|
|
733
|
+
`stale-while-revalidate` observers track the entire background request the same
|
|
734
|
+
way, including failures. Disabled and cache-only observers reject explicit
|
|
735
|
+
refetches without fetching; enable the request or change its mode first.
|
|
736
|
+
|
|
737
|
+
```ts
|
|
738
|
+
const state = useRequestState(request, { mode: 'stale-while-revalidate' });
|
|
739
|
+
// Render state.data, state.isFetching, and state.error independently.
|
|
740
|
+
await state.refetch(); // Also usable from a refresh button or retry action.
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
Overlapping reads use their network start order when normalizing responses. An
|
|
744
|
+
older response cannot overwrite a field already supplied by a newer response or
|
|
745
|
+
a later confirmed write. It may still fill other fields. Root results and list
|
|
746
|
+
replacements also retain the newer result. Optimistic layers remain visible over
|
|
747
|
+
incoming confirmed data; rolling them back reveals that data, while committing a
|
|
748
|
+
mutation protects its confirmed fields from older requests.
|
|
749
|
+
|
|
750
|
+
#### Aliases
|
|
751
|
+
|
|
752
|
+
Use `alias(sourceField, selection)` to select one schema field under multiple
|
|
753
|
+
result names, with independent arguments:
|
|
754
|
+
|
|
755
|
+
```ts
|
|
756
|
+
const GamesView = view<User>()({
|
|
757
|
+
activeGames: alias('games', {
|
|
758
|
+
args: { first: 20, status: 'Active' },
|
|
759
|
+
items: { node: GameView },
|
|
760
|
+
pagination: { hasNext: true, nextCursor: true },
|
|
761
|
+
}),
|
|
762
|
+
waitingGames: alias('games', {
|
|
763
|
+
args: { first: 20, status: 'Waiting' },
|
|
764
|
+
items: { node: GameView },
|
|
765
|
+
}),
|
|
766
|
+
displayName: alias('name', true),
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
const request = { currentUser: alias('viewer', { view: GamesView }) };
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
Source fields, nested selections, result types, and generated root argument
|
|
773
|
+
contracts remain checked. Alias names affect the returned shape; normalized
|
|
774
|
+
identity uses the source field and its arguments. An ordinary selection with the
|
|
775
|
+
same arguments shares the cached data. Connection filters identify separate
|
|
776
|
+
cache slots and pagination state. Conflicting response names or arguments in a
|
|
777
|
+
composed view fail during planning. `id` and `__typename` are reserved result
|
|
778
|
+
names because fate needs them for entity identity.
|
|
779
|
+
|
|
780
|
+
GraphQL emits native aliases, including inside mutation selections, so multiple
|
|
781
|
+
variants execute in one operation. Native HTTP, tRPC, and custom transports
|
|
782
|
+
receive compatible reads with ordinary schema field names; conflicting variants
|
|
783
|
+
may require multiple reads or subscriptions. These fallback reads are independent
|
|
784
|
+
server operations. A mutation that needs multiple variants requires native alias
|
|
785
|
+
support and otherwise rejects before executing. A simple rename is lowered for
|
|
786
|
+
native mutations without repeating the action.
|
|
787
|
+
|
|
788
|
+
Custom transports can set `supportsAliases: true` to handle the full selection in
|
|
789
|
+
one operation. Selection path segments and corresponding argument keys then use
|
|
790
|
+
`resultName:schemaField`, and returned records must use those same encoded keys.
|
|
791
|
+
GraphQL performs that conversion automatically. Applications consume the declared
|
|
792
|
+
result names, never the encoded transport names.
|
|
793
|
+
|
|
616
794
|
## Deferred Views
|
|
617
795
|
|
|
618
796
|
Use `defer` when a field should not block the parent view. The parent view receives a deferred handle immediately after the eager fields are available, and the component that reads that handle with `useView`, `useListView`, or `useLiveListView` decides which `Suspense` boundary handles the loading state.
|
|
@@ -1525,7 +1703,7 @@ Native HTTP sends durable mutations and receipt-only recovery using protocol ver
|
|
|
1525
1703
|
|
|
1526
1704
|
### tRPC, GraphQL, and Custom Transports
|
|
1527
1705
|
|
|
1528
|
-
For tRPC, GraphQL, or a custom transport, provide a `mutateDurably(name, input, select, identity)` method that sends the identity to an endpoint with server deduplication. Regular calls continue to use `mutate(name, input, select)`.
|
|
1706
|
+
For tRPC, GraphQL, or a custom transport, provide a `mutateDurably(name, input, select, identity)` method that sends the identity to an endpoint with server deduplication. Regular calls continue to use `mutate(name, input, select)`. GraphQL passes selected field arguments as an optional fifth parameter to `mutateDurably` (fourth for `mutate`), separate from the mutation input. Forward that parameter if the durable response selects fields with arguments.
|
|
1529
1707
|
|
|
1530
1708
|
Both `createTRPCTransport` and `createGraphQLTransport`, including their generated clients, accept `mutateDurably`. For example, you can use a native fate endpoint for durable mutations alongside your existing transport:
|
|
1531
1709
|
|
|
@@ -1934,7 +2112,7 @@ The client-only template is the smallest reference for the integration. It conta
|
|
|
1934
2112
|
The GraphQL transport expects a schema with Relay-style object identity and pagination:
|
|
1935
2113
|
|
|
1936
2114
|
- Entity objects include `id` and `__typename`.
|
|
1937
|
-
- Object fetches
|
|
2115
|
+
- Object fetches use `nodes(ids:)` by default, or configured per-type query fields.
|
|
1938
2116
|
- List fields return Relay connections with `edges`, `cursor`, `node`, and `pageInfo`.
|
|
1939
2117
|
- Root queries and mutations return the entity type selected by the fate view.
|
|
1940
2118
|
|
|
@@ -2113,6 +2291,31 @@ GraphQL operations issued in the same microtask are batched into a single GraphQ
|
|
|
2113
2291
|
|
|
2114
2292
|
Deferred view fields work with the GraphQL transport through the same normalized cache flow as native HTTP: the eager query omits `defer(...)` fields, and `useView`, `useListView`, or `useLiveListView` fetches the missing selection through `nodes(ids:)` when the deferred handle is read. GraphQL `@defer` is the natural wire format for this feature, but fate's GraphQL transport currently expects one JSON result per operation and does not consume incremental multipart patches yet.
|
|
2115
2293
|
|
|
2294
|
+
### Refetch Fields
|
|
2295
|
+
|
|
2296
|
+
For servers without `nodes(ids:)`, configure a query field for each refetchable entity type:
|
|
2297
|
+
|
|
2298
|
+
```tsx
|
|
2299
|
+
export const fateGraphQL = {
|
|
2300
|
+
byId: {
|
|
2301
|
+
Game: { field: 'fetch__Game' },
|
|
2302
|
+
User: { field: 'fetch__User', idArg: 'id' },
|
|
2303
|
+
},
|
|
2304
|
+
nodes: false,
|
|
2305
|
+
// Keep your schema, roots, and mutations here.
|
|
2306
|
+
} as const;
|
|
2307
|
+
```
|
|
2308
|
+
|
|
2309
|
+
`idArg` defaults to `id`. Each mapped field must return one entity of the named type, optionally nullable. The adapter batches multiple IDs into aliased query fields in one request, preserves input order for returned records, and omits null results. Node ID encoding and decoding apply to mapped fetches too.
|
|
2310
|
+
|
|
2311
|
+
Mappings take precedence over the default `nodes(ids:)` fallback. Set `nodes: false` to disable that fallback; fetching an unmapped type then reports a configuration error without issuing a request. Existing clients need no configuration changes. Direct users of `createGraphQLTransport` can pass the same `byId` and `nodes` options.
|
|
2312
|
+
|
|
2313
|
+
When `schema` is supplied, codegen and transport construction validate the mapping: the field must exist, its ID argument must accept a single `ID`, `String`, or `Int`, its result must match the concrete entity type, and any other arguments must be optional or have defaults. Schema metadata also supplies the wire variable types. Without a schema, identifiers are validated and arguments use GraphQL literals.
|
|
2314
|
+
|
|
2315
|
+
Nested pagination uses the owner ID only for the mapped query field. Pagination and filter arguments remain on the connection field. For example, fetching another page of a user's games produces `fetch__User(id: $owner) { games(first: $count, after: $cursor) { ... } }`.
|
|
2316
|
+
|
|
2317
|
+
Choose fields with the appropriate authorization rules for the consuming views. The adapter does not switch between public and authenticated lookup fields automatically.
|
|
2318
|
+
|
|
2116
2319
|
### Object IDs
|
|
2117
2320
|
|
|
2118
2321
|
The transport converts between fate entity IDs and GraphQL node IDs. By default, it sends IDs as `${type}-${id}` and strips that prefix from returned IDs. Override this if your schema uses Relay global IDs, raw database IDs, or another encoding:
|
|
@@ -2253,6 +2456,80 @@ The GraphQL transport is intentionally a mapping layer. It does not require `cre
|
|
|
2253
2456
|
|
|
2254
2457
|
Use data views to expose only the fields the client should be able to select, keep GraphQL schema authorization in your server, and treat `src/fate/graphql.ts` as the contract between your GraphQL API and fate's client.
|
|
2255
2458
|
|
|
2459
|
+
### Schema-Aware Arguments
|
|
2460
|
+
|
|
2461
|
+
Provide your server's schema in the mapping module to generate GraphQL argument contracts and send arguments as typed variables. The `schema` option accepts SDL text or a `GraphQLSchema` object and is evaluated during client generation:
|
|
2462
|
+
|
|
2463
|
+
```tsx
|
|
2464
|
+
import { readFileSync } from 'node:fs';
|
|
2465
|
+
|
|
2466
|
+
export const fateGraphQL = {
|
|
2467
|
+
schema: readFileSync(new URL('./schema.graphql', import.meta.url), 'utf8'),
|
|
2468
|
+
roots: {
|
|
2469
|
+
maps: { field: 'maps' },
|
|
2470
|
+
},
|
|
2471
|
+
mutations: {
|
|
2472
|
+
'map.update': graphqlMutation<Map, UpdateMapInput, Map>('Map', {
|
|
2473
|
+
field: 'updateMap',
|
|
2474
|
+
inputArg: false,
|
|
2475
|
+
}),
|
|
2476
|
+
},
|
|
2477
|
+
};
|
|
2478
|
+
```
|
|
2479
|
+
|
|
2480
|
+
Install `graphql` in the package performing generation when passing SDL. Run `fate generate` again after updating the schema. Generated browser code contains argument metadata, not the SDL or GraphQL parser. Existing clients without a schema retain literal argument serialization; use schema-aware generation for enums.
|
|
2481
|
+
|
|
2482
|
+
SDL text must include definitions for every directive it uses. Some server-generated or Relay-oriented schema files contain annotations such as `@auth` or `@fetchable` without declaring them; strict SDL parsing rejects those files. Prefer adding the directive definitions to the exported SDL. If the omissions are intentional, pass a schema object instead:
|
|
2483
|
+
|
|
2484
|
+
```tsx
|
|
2485
|
+
import { readFileSync } from 'node:fs';
|
|
2486
|
+
import { buildSchema } from 'graphql';
|
|
2487
|
+
|
|
2488
|
+
const sdl = readFileSync(new URL('./schema.graphql', import.meta.url), 'utf8');
|
|
2489
|
+
export const fateGraphQL = {
|
|
2490
|
+
schema: buildSchema(sdl, { assumeValidSDL: true }),
|
|
2491
|
+
// ...roots and mutations...
|
|
2492
|
+
};
|
|
2493
|
+
```
|
|
2494
|
+
|
|
2495
|
+
`assumeValidSDL` skips SDL validation, so use it only when the annotations are known and the schema is validated elsewhere. Fate still extracts argument metadata at generation time; the browser bundle does not include the schema object.
|
|
2496
|
+
|
|
2497
|
+
For example, given `maps(biome: Biome)`, an argument `{ biome: 'Grassland' }` is sent as a variable declared with type `Biome`, rather than an invalid quoted enum literal. Variables also work for nested field arguments, input objects, lists, node IDs, and mutations. Batched operations use distinct variable names and separate query and mutation payloads. Mutation selection arguments are kept out of mutation inputs. A GraphQL mutation argument named `args` belongs in `input` like any other wire argument; use the mutation's `view` or its separate `args` option for selected field arguments. When calling `transport.mutate` directly, pass selected field arguments as its optional fourth parameter.
|
|
2498
|
+
|
|
2499
|
+
The generated client enforces root arguments in `client.request` and the React/Vue `useRequest` adapters. Mutation inputs are derived from the schema using the configured `inputArg`, replacing the manually declared input type for generated clients. Non-null arguments are required unless the schema supplies a default. Omitting an optional/defaulted value preserves its server default; explicit `null` remains distinct.
|
|
2500
|
+
|
|
2501
|
+
Generation also exports `GraphQLInputs`, `GraphQLFieldArguments`, and `GraphQLSelectionArguments` from `.fate/client.generated.ts`. Use field contracts to check arguments inside reusable views:
|
|
2502
|
+
|
|
2503
|
+
```tsx
|
|
2504
|
+
import type { GraphQLFieldArguments } from '../.fate/client.generated.ts';
|
|
2505
|
+
|
|
2506
|
+
const stateArgs = {
|
|
2507
|
+
biome: 'Grassland',
|
|
2508
|
+
} satisfies GraphQLFieldArguments['Map']['state'];
|
|
2509
|
+
|
|
2510
|
+
const MapView = view<Map>()({
|
|
2511
|
+
id: true,
|
|
2512
|
+
state: { args: stateArgs },
|
|
2513
|
+
});
|
|
2514
|
+
```
|
|
2515
|
+
|
|
2516
|
+
At runtime the transport checks selected field names, argument names, enum values, required inputs, built-in scalar types, and nested input objects before issuing an HTTP request. Custom scalar values have TypeScript type `unknown` and remain subject to server validation. These contracts cover arguments; they do not add support for GraphQL result shapes that the transport otherwise does not support.
|
|
2517
|
+
|
|
2518
|
+
For a manually constructed transport, extract metadata on the build/server side with `createGraphQLArgumentSchema` from `@nkzw/fate/vite`, then pass the serialized result as the transport's `schema` option. Keep that build-time helper out of browser modules.
|
|
2519
|
+
|
|
2520
|
+
### Errors and Partial Results
|
|
2521
|
+
|
|
2522
|
+
A failed GraphQL operation rejects with `GraphQLRequestError`. It preserves all
|
|
2523
|
+
GraphQL `errors`, the first error's `path` and `extensions`, and the HTTP `status`
|
|
2524
|
+
when available. HTTP errors use the same error type; network failures retain the
|
|
2525
|
+
original fetch error.
|
|
2526
|
+
|
|
2527
|
+
Partial response data is available as `error.data` for explicit application
|
|
2528
|
+
handling. It is unnormalized diagnostic data, not a successful typed result, and
|
|
2529
|
+
fate does not write it to the cache. Successful sibling operations in the same
|
|
2530
|
+
batch still resolve normally, including legitimate `null` results. Mutation
|
|
2531
|
+
functions retain their `{ result, error }` result contract.
|
|
2532
|
+
|
|
2256
2533
|
## Server Integration
|
|
2257
2534
|
|
|
2258
2535
|
Until now, we have focused on the client-side API of fate. You'll need a backend that can be wired into fate's typed request model so the Vite plugin can connect the typed fate APIs to your app. _fate_ currently ships three integration paths:
|
package/lib/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConnectionRef, Deferred, Deferred as Deferred$1, FateClient as FateClient$1, FateDehydratedState, FateMutations, FateRoots, GraphQLMutationDefinition, GraphQLMutationInput, GraphQLMutationMap, GraphQLMutationOutput, GraphQLTransportOptions, HydrateOptions, HydrationLimits, InferFateAPI, Pagination, Pagination as Pagination$1, Persistence, PersistenceSession, PersistenceSnapshot, Request, RequestOptions, RequestResult, View, ViewData, ViewEntity, ViewEntityName, ViewRef, ViewRef as ViewRef$1, ViewSelection, clientRoot, createClient, createGraphQLTransport, createHTTPTransport, createTRPCTransport, defer, graphqlMutation, mutation, toEntityId, view } from "@nkzw/fate";
|
|
1
|
+
import { AliasedSelection, AliasedView, CheckedRequest, ConditionalSelection, ConnectionRef, Deferred, Deferred as Deferred$1, FateClient as FateClient$1, FateDehydratedState, FateMutations, FateRoots, GraphQLMutationDefinition, GraphQLMutationInput, GraphQLMutationMap, GraphQLMutationOutput, GraphQLTransportOptions, HydrateOptions, HydrationLimits, InferFateAPI, Pagination, Pagination as Pagination$1, ParameterizedView, Persistence, PersistenceSession, PersistenceSnapshot, Request, RequestOptions, RequestResult, View, ViewData, ViewEntity, ViewEntityName, ViewRef, ViewRef as ViewRef$1, ViewSelection, alias, clientRoot, createClient, createGraphQLTransport, createHTTPTransport, createTRPCTransport, defer, graphqlMutation, mutation, toEntityId, view, when } from "@nkzw/fate";
|
|
2
2
|
import { ComputedRef, InjectionKey, MaybeRefOrGetter, Plugin, PropType, Ref, ShallowRef } from "vue";
|
|
3
3
|
//#region src/useRequest.d.ts
|
|
4
4
|
type GeneratedFateClient$1 = ReturnType<typeof import('vue-fate/client').createFateClient>;
|
|
@@ -14,7 +14,7 @@ type RequestResource<T> = {
|
|
|
14
14
|
/**
|
|
15
15
|
* Declares the data a component or screen needs and exposes it as a Vue resource.
|
|
16
16
|
*/
|
|
17
|
-
export declare function useRequest<R extends Request, O extends FateRoots = Roots>(request: MaybeRefOrGetter<R
|
|
17
|
+
export declare function useRequest<const R extends Request, O extends FateRoots = Roots>(request: MaybeRefOrGetter<CheckedRequest<O, R>>, options?: RequestOptions): RequestResource<RequestResult<O, R>>;
|
|
18
18
|
//#endregion
|
|
19
19
|
//#region src/context.d.ts
|
|
20
20
|
type GeneratedFateClient = ReturnType<typeof import('vue-fate/client').createFateClient>;
|
|
@@ -97,9 +97,9 @@ type ViewResource<T> = ShallowRef<T> & {
|
|
|
97
97
|
/**
|
|
98
98
|
* Resolves a reference against a view and subscribes to updates for that selection.
|
|
99
99
|
*/
|
|
100
|
-
export declare function useView<V extends View<any, any>, R extends ViewRef$1<ViewEntityName<V>> | null>(view: V, ref: MaybeRefOrGetter<R>): ViewResource<R extends null ?
|
|
101
|
-
export declare function useView<V extends View<any, any>, R extends Deferred$1<ViewRef$1<ViewEntityName<V>>> | null>(view: V, ref: MaybeRefOrGetter<R>): ViewResource<R extends null ?
|
|
102
|
-
export declare function useView<V extends View<any, any>>(view: V, ref: MaybeRefOrGetter<Deferred$1<ViewRef$1<ViewEntityName<V>>> | ViewRef$1<ViewEntityName<V>> | null>): ViewResource<ViewData<ViewEntityWithTypename$1<V>, ViewSelection<V>> | null>;
|
|
100
|
+
export declare function useView<V extends View<any, any>, R extends ViewRef$1<ViewEntityName<V>> | null | undefined>(view: V, ref: MaybeRefOrGetter<R>): ViewResource<R extends null | undefined ? R : ViewData<ViewEntityWithTypename$1<V>, ViewSelection<V>>>;
|
|
101
|
+
export declare function useView<V extends View<any, any>, R extends Deferred$1<ViewRef$1<ViewEntityName<V>>> | null | undefined>(view: V, ref: MaybeRefOrGetter<R>): ViewResource<R extends null | undefined ? R : ViewData<ViewEntityWithTypename$1<V>, ViewSelection<V>>>;
|
|
102
|
+
export declare function useView<V extends View<any, any>>(view: V, ref: MaybeRefOrGetter<Deferred$1<ViewRef$1<ViewEntityName<V>>> | ViewRef$1<ViewEntityName<V>> | null | undefined>): ViewResource<ViewData<ViewEntityWithTypename$1<V>, ViewSelection<V>> | null | undefined>;
|
|
103
103
|
//#endregion
|
|
104
104
|
//#region src/useLiveView.d.ts
|
|
105
105
|
type ViewEntityWithTypename<V extends View<any, any>> = ViewEntity<V> & {
|
|
@@ -108,8 +108,8 @@ type ViewEntityWithTypename<V extends View<any, any>> = ViewEntity<V> & {
|
|
|
108
108
|
/**
|
|
109
109
|
* Resolves a reference against a view and subscribes to live server updates for that selection.
|
|
110
110
|
*/
|
|
111
|
-
export declare function useLiveView<V extends View<any, any>, R extends ViewRef$1<ViewEntityName<V>> | null>(view: V, ref: MaybeRefOrGetter<R>): ViewResource<R extends null ?
|
|
112
|
-
export declare function useLiveView<V extends View<any, any>, R extends Deferred$1<ViewRef$1<ViewEntityName<V>>> | null>(view: V, ref: MaybeRefOrGetter<R>): ViewResource<R extends null ?
|
|
113
|
-
export declare function useLiveView<V extends View<any, any>>(view: V, ref: MaybeRefOrGetter<Deferred$1<ViewRef$1<ViewEntityName<V>>> | ViewRef$1<ViewEntityName<V>> | null>): ViewResource<ViewData<ViewEntityWithTypename<V>, ViewSelection<V>> | null>;
|
|
111
|
+
export declare function useLiveView<V extends View<any, any>, R extends ViewRef$1<ViewEntityName<V>> | null | undefined>(view: V, ref: MaybeRefOrGetter<R>): ViewResource<R extends null | undefined ? R : ViewData<ViewEntityWithTypename<V>, ViewSelection<V>>>;
|
|
112
|
+
export declare function useLiveView<V extends View<any, any>, R extends Deferred$1<ViewRef$1<ViewEntityName<V>>> | null | undefined>(view: V, ref: MaybeRefOrGetter<R>): ViewResource<R extends null | undefined ? R : ViewData<ViewEntityWithTypename<V>, ViewSelection<V>>>;
|
|
113
|
+
export declare function useLiveView<V extends View<any, any>>(view: V, ref: MaybeRefOrGetter<Deferred$1<ViewRef$1<ViewEntityName<V>>> | ViewRef$1<ViewEntityName<V>> | null | undefined>): ViewResource<ViewData<ViewEntityWithTypename<V>, ViewSelection<V>> | null | undefined>;
|
|
114
114
|
//#endregion
|
|
115
|
-
export { type ConnectionRef, type Deferred, type FateDehydratedState, type GraphQLMutationDefinition, type GraphQLMutationInput, type GraphQLMutationMap, type GraphQLMutationOutput, type GraphQLTransportOptions, type HydrateOptions, type HydrationLimits, type InferFateAPI, type ListViewState, type Pagination, type Persistence, type PersistenceSession, type PersistenceSnapshot, type RequestResource, type ViewRef, type ViewResource, clientRoot, createClient, createGraphQLTransport, createHTTPTransport, createTRPCTransport, defer, graphqlMutation, mutation, toEntityId, view };
|
|
115
|
+
export { type AliasedSelection, type AliasedView, type ConditionalSelection, type ConnectionRef, type Deferred, type FateDehydratedState, type GraphQLMutationDefinition, type GraphQLMutationInput, type GraphQLMutationMap, type GraphQLMutationOutput, type GraphQLTransportOptions, type HydrateOptions, type HydrationLimits, type InferFateAPI, type ListViewState, type Pagination, type ParameterizedView, type Persistence, type PersistenceSession, type PersistenceSnapshot, type RequestResource, type ViewRef, type ViewResource, alias, clientRoot, createClient, createGraphQLTransport, createHTTPTransport, createTRPCTransport, defer, graphqlMutation, mutation, toEntityId, view, when };
|
package/lib/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConnectionTag, clientRoot, createClient, createGraphQLTransport, createHTTPTransport, createTRPCTransport, defer, graphqlMutation, isDeferred, isViewTag, mutation, toEntityId, view } from "@nkzw/fate";
|
|
1
|
+
import { ConnectionTag, alias, clientRoot, createClient, createGraphQLTransport, createHTTPTransport, createTRPCTransport, defer, graphqlMutation, isDeferred, isViewTag, mutation, resolveView, toEntityId, view, when } from "@nkzw/fate";
|
|
2
2
|
import { computed, defineComponent, inject, isRef, markRaw, onScopeDispose, onServerPrefetch, provide, shallowRef, toValue, watch } from "vue";
|
|
3
3
|
import { getListEntries } from "@nkzw/fate/list";
|
|
4
4
|
//#region src/context.ts
|
|
@@ -273,9 +273,10 @@ const isFulfilledThenable = (value) => "status" in value && value.status === "fu
|
|
|
273
273
|
//#endregion
|
|
274
274
|
//#region src/useView.ts
|
|
275
275
|
const nullSnapshot = fulfilledThenable(null);
|
|
276
|
+
const undefinedSnapshot = fulfilledThenable(void 0);
|
|
276
277
|
function useView(view, ref) {
|
|
277
278
|
const clientSource = getFateClientSource(useFateClient());
|
|
278
|
-
const data = shallowRef(null);
|
|
279
|
+
const data = shallowRef(toValue(ref) === void 0 ? void 0 : null);
|
|
279
280
|
const errorState = shallowRef(null);
|
|
280
281
|
const pending = shallowRef(false);
|
|
281
282
|
let disposed = false;
|
|
@@ -309,7 +310,7 @@ function useView(view, ref) {
|
|
|
309
310
|
};
|
|
310
311
|
const getSnapshot = () => {
|
|
311
312
|
const currentRef = toValue(ref);
|
|
312
|
-
if (currentRef
|
|
313
|
+
if (currentRef == null) return currentRef === void 0 ? undefinedSnapshot : nullSnapshot;
|
|
313
314
|
if (!isDeferred(currentRef)) return readViewSnapshot(currentRef);
|
|
314
315
|
const deferredSnapshot = activeClient.readDeferred(currentRef);
|
|
315
316
|
if (isFulfilledThenable(deferredSnapshot)) {
|
|
@@ -318,7 +319,7 @@ function useView(view, ref) {
|
|
|
318
319
|
coverage: deferredSnapshot.value.coverage,
|
|
319
320
|
data: null
|
|
320
321
|
});
|
|
321
|
-
return readViewSnapshot(activeClient.ref(resolvedRef.__typename, resolvedRef.id, view), deferredSnapshot.value.coverage);
|
|
322
|
+
return readViewSnapshot(activeClient.ref(resolvedRef.__typename, resolvedRef.id, resolveView(view, resolvedRef)), deferredSnapshot.value.coverage);
|
|
322
323
|
}
|
|
323
324
|
return Promise.resolve(deferredSnapshot).then((deferredValue) => {
|
|
324
325
|
const resolvedRef = deferredValue.data;
|
|
@@ -326,7 +327,7 @@ function useView(view, ref) {
|
|
|
326
327
|
coverage: deferredValue.coverage,
|
|
327
328
|
data: null
|
|
328
329
|
};
|
|
329
|
-
return readViewSnapshot(activeClient.ref(resolvedRef.__typename, resolvedRef.id, view), deferredValue.coverage);
|
|
330
|
+
return readViewSnapshot(activeClient.ref(resolvedRef.__typename, resolvedRef.id, resolveView(view, resolvedRef)), deferredValue.coverage);
|
|
330
331
|
});
|
|
331
332
|
};
|
|
332
333
|
function refresh() {
|
|
@@ -342,12 +343,12 @@ function useView(view, ref) {
|
|
|
342
343
|
const nextSnapshot = getSnapshot();
|
|
343
344
|
currentPromise = Promise.resolve(nextSnapshot).then((value) => {
|
|
344
345
|
if (!disposed && currentToken === token) {
|
|
345
|
-
snapshot = value;
|
|
346
|
-
data.value = value ? markRaw(value.data) :
|
|
346
|
+
snapshot = value ?? null;
|
|
347
|
+
data.value = value ? markRaw(value.data) : value;
|
|
347
348
|
pending.value = false;
|
|
348
349
|
updateSubscriptions();
|
|
349
350
|
}
|
|
350
|
-
return value ? value.data :
|
|
351
|
+
return value ? value.data : value;
|
|
351
352
|
}, (error) => {
|
|
352
353
|
if (!disposed && currentToken === token) {
|
|
353
354
|
errorState.value = error;
|
|
@@ -401,8 +402,7 @@ function useLiveView(view, ref) {
|
|
|
401
402
|
resolveRef().then((resolvedRef) => {
|
|
402
403
|
if (disposed || currentToken !== token || !resolvedRef) return;
|
|
403
404
|
const client = clientSource.value;
|
|
404
|
-
client.
|
|
405
|
-
liveUnsubscribe = client.subscribeLiveView(view, client.ref(resolvedRef.__typename, resolvedRef.id, view));
|
|
405
|
+
liveUnsubscribe = client.subscribeLiveView(view, client.ref(resolvedRef.__typename, resolvedRef.id, resolveView(view, resolvedRef)));
|
|
406
406
|
}).catch((error) => {
|
|
407
407
|
if (!disposed && currentToken === token) {
|
|
408
408
|
viewResource.error.value = error;
|
|
@@ -499,4 +499,4 @@ function useRequest(request, options) {
|
|
|
499
499
|
};
|
|
500
500
|
}
|
|
501
501
|
//#endregion
|
|
502
|
-
export { FateClient, FateClientKey, clientRoot, createClient, createFatePlugin, createGraphQLTransport, createHTTPTransport, createTRPCTransport, defer, graphqlMutation, mutation, provideFateClient, toEntityId, useFateClient, useListView, useLiveListView, useLiveView, useRequest, useView, view };
|
|
502
|
+
export { FateClient, FateClientKey, alias, clientRoot, createClient, createFatePlugin, createGraphQLTransport, createHTTPTransport, createTRPCTransport, defer, graphqlMutation, mutation, provideFateClient, toEntityId, useFateClient, useListView, useLiveListView, useLiveView, useRequest, useView, view, when };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vue-fate",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "fate is a modern data client for Vue.",
|
|
5
5
|
"homepage": "https://github.com/nkzw-tech/fate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@nkzw/fate": "1.
|
|
43
|
+
"@nkzw/fate": "1.7.1"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"vue": "^3.5.
|
|
46
|
+
"vue": "^3.5.43"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"vue": "^3.5.0"
|