eden-tanstack-query 0.0.6 → 0.0.8
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 +77 -7
- package/dist/index.d.ts +21 -7
- package/dist/index.js +16 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -165,34 +165,104 @@ const eden = createEdenQuery<App>('http://localhost:8080', {
|
|
|
165
165
|
When `throwOnError` is `true` (the default), errors are thrown before reaching callbacks like `select` and `onSuccess`. The library automatically narrows the data type to exclude error shapes:
|
|
166
166
|
|
|
167
167
|
```typescript
|
|
168
|
-
//
|
|
169
|
-
//
|
|
168
|
+
// Given an endpoint that returns:
|
|
169
|
+
// - Success: { users: User[] }
|
|
170
|
+
// - Error: { error: string }
|
|
171
|
+
|
|
172
|
+
// Default: throwOnError = true (or omitted)
|
|
170
173
|
const eden = createEdenQuery<App>('http://localhost:8080')
|
|
174
|
+
// OR explicitly:
|
|
175
|
+
const eden = createEdenQuery<App>('http://localhost:8080', { throwOnError: true })
|
|
171
176
|
|
|
172
177
|
useQuery(eden.users.get.queryOptions(undefined, {
|
|
173
|
-
select: (data) =>
|
|
178
|
+
select: (data) => {
|
|
179
|
+
// data: { users: User[] }
|
|
180
|
+
// Error shape is excluded - errors throw before reaching here
|
|
181
|
+
return data.users // No type guard needed
|
|
182
|
+
},
|
|
174
183
|
onSuccess: (data) => {
|
|
175
|
-
|
|
184
|
+
// data: { users: User[] }
|
|
185
|
+
console.log(data.users)
|
|
176
186
|
}
|
|
177
187
|
}))
|
|
188
|
+
|
|
189
|
+
// query.data type: { users: User[] } | undefined
|
|
178
190
|
```
|
|
179
191
|
|
|
180
|
-
When `throwOnError` is `false
|
|
192
|
+
When `throwOnError` is `false`, the full union type is preserved since errors don't throw:
|
|
181
193
|
|
|
182
194
|
```typescript
|
|
183
195
|
// Explicit: throwOnError = false
|
|
184
|
-
// Data type includes all response shapes (success and error)
|
|
185
196
|
const eden = createEdenQuery<App>('http://localhost:8080', {
|
|
186
197
|
throwOnError: false
|
|
187
198
|
})
|
|
188
199
|
|
|
189
200
|
useQuery(eden.users.get.queryOptions(undefined, {
|
|
190
201
|
select: (data) => {
|
|
191
|
-
//
|
|
202
|
+
// data: { users: User[] } | { error: string }
|
|
203
|
+
// Must handle both cases since errors don't throw
|
|
192
204
|
if ('error' in data) return null
|
|
193
205
|
return data.users
|
|
206
|
+
},
|
|
207
|
+
onSuccess: (data) => {
|
|
208
|
+
// data: { users: User[] } | { error: string }
|
|
209
|
+
if ('error' in data) {
|
|
210
|
+
console.log('Error:', data.error)
|
|
211
|
+
} else {
|
|
212
|
+
console.log(data.users)
|
|
213
|
+
}
|
|
194
214
|
}
|
|
195
215
|
}))
|
|
216
|
+
|
|
217
|
+
// query.data type: { users: User[] } | { error: string } | undefined
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
When `throwOnError` is a function, the full union type is also preserved (since throwing is conditional):
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// Conditional throwing
|
|
224
|
+
const eden = createEdenQuery<App>('http://localhost:8080', {
|
|
225
|
+
throwOnError: (queryKey, status) => status >= 500
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
useQuery(eden.users.get.queryOptions(undefined, {
|
|
229
|
+
select: (data) => {
|
|
230
|
+
// data: { users: User[] } | { error: string }
|
|
231
|
+
// Full union - some errors may not throw (e.g., 404)
|
|
232
|
+
if ('error' in data) return null
|
|
233
|
+
return data.users
|
|
234
|
+
}
|
|
235
|
+
}))
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Known Limitation: createQuery Inference
|
|
239
|
+
|
|
240
|
+
The type narrowing works correctly at the library level (queryFn return type), but TanStack Query's `createQuery`/`useQuery` may not always infer the narrowed type due to complex generic inference.
|
|
241
|
+
|
|
242
|
+
**What works:**
|
|
243
|
+
- `NarrowedData` type correctly excludes error shapes
|
|
244
|
+
- `queryFn` return type is narrowed at the library level
|
|
245
|
+
- Query keys are correctly typed
|
|
246
|
+
- Direct access to options properties
|
|
247
|
+
|
|
248
|
+
**What may vary:**
|
|
249
|
+
- `createQuery(options)` inference depends on framework version and TS config
|
|
250
|
+
- `query.data` may show as `unknown` or full union in some cases
|
|
251
|
+
|
|
252
|
+
**Workarounds:**
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// 1. Use select to transform with explicit types
|
|
256
|
+
useQuery(eden.users.get.queryOptions(undefined, {
|
|
257
|
+
select: (data) => data.users // data is narrowed here
|
|
258
|
+
}))
|
|
259
|
+
|
|
260
|
+
// 2. Add explicit type annotation
|
|
261
|
+
const query = useQuery(eden.users.get.queryOptions()) as CreateQueryResult<{ users: User[] }>
|
|
262
|
+
|
|
263
|
+
// 3. Access queryFn directly for fully typed results
|
|
264
|
+
const options = eden.users.get.queryOptions()
|
|
265
|
+
const data = await options.queryFn() // Correctly typed
|
|
196
266
|
```
|
|
197
267
|
|
|
198
268
|
## Advanced Usage
|
package/dist/index.d.ts
CHANGED
|
@@ -33,15 +33,32 @@ interface EdenQueryConfig<TThrowOnError extends ThrowOnErrorOption = true> {
|
|
|
33
33
|
* Narrows data type by excluding error-shaped objects when throwOnError is true.
|
|
34
34
|
* When throwOnError is true, errors are thrown before reaching callbacks like
|
|
35
35
|
* `select` or `onSuccess`, so the data type should exclude error shapes.
|
|
36
|
+
*
|
|
37
|
+
* Uses conditional type to check if each union member has an `error: string` property,
|
|
38
|
+
* which handles cases like `{ error: string; message?: undefined }` that simple
|
|
39
|
+
* `Exclude<T, { error: string }>` misses due to structural typing quirks.
|
|
36
40
|
*/
|
|
37
|
-
type NarrowedData<TData, TThrowOnError> = TThrowOnError extends true ?
|
|
41
|
+
type NarrowedData<TData, TThrowOnError> = TThrowOnError extends true ? TData extends {
|
|
38
42
|
error: string;
|
|
39
|
-
}
|
|
43
|
+
} ? never : TData : TData;
|
|
40
44
|
interface EdenOptions<TEden = unknown> {
|
|
41
45
|
eden?: TEden;
|
|
42
46
|
}
|
|
43
47
|
type EdenQueryKey<TInput = unknown, TMethod extends HTTPMethod = HTTPMethod> = readonly [...string[], TInput, TMethod];
|
|
48
|
+
/**
|
|
49
|
+
* Options for eden queries, extending QueryObserverOptions.
|
|
50
|
+
* Used for the optional `options` parameter in `queryOptions()`.
|
|
51
|
+
*/
|
|
44
52
|
type EdenQueryOptions<TQueryFnData, TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TEden = unknown> = Omit<QueryObserverOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>, 'queryKey' | 'queryFn'> & EdenOptions<TEden>;
|
|
53
|
+
/**
|
|
54
|
+
* Full query options returned by `queryOptions()`, compatible with createQuery.
|
|
55
|
+
* This type uses a simpler structure to ensure TypeScript properly infers TData
|
|
56
|
+
* when passed to createQuery.
|
|
57
|
+
*/
|
|
58
|
+
type EdenQueryOptionsResult<TQueryFnData, TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey> = {
|
|
59
|
+
queryKey: TQueryKey;
|
|
60
|
+
queryFn: (context: QueryFunctionContext<TQueryKey>) => Promise<TQueryFnData>;
|
|
61
|
+
} & Omit<QueryObserverOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>, 'queryKey' | 'queryFn'>;
|
|
45
62
|
type EdenMutationOptions<TData, TError = Error, TVariables = void, TContext = unknown, TEden = unknown> = Omit<MutationOptions<TData, TError, TVariables, TContext>, 'mutationFn'> & EdenOptions<TEden>;
|
|
46
63
|
type EdenInfiniteQueryOptions<TQueryFnData, TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, TEden = unknown> = Omit<InfiniteQueryObserverOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>, 'queryKey' | 'queryFn'> & EdenOptions<TEden>;
|
|
47
64
|
type EdenPartial<T> = T extends object ? Partial<T> : never;
|
|
@@ -56,10 +73,7 @@ type EdenData<T> = Extract<AwaitedReturn<T>, {
|
|
|
56
73
|
type EdenQueryMethod<TMethod extends Extract<HTTPMethod, 'get' | 'head'>, TCall extends (...args: any[]) => Promise<any>, TThrowOnError extends ThrowOnErrorOption = true> = TCall & {
|
|
57
74
|
queryKey: (input?: FirstArg<TCall>) => EdenQueryKey<FirstArg<TCall>, TMethod>;
|
|
58
75
|
queryFilter: (input?: FirstArg<TCall>) => QueryFilters<EdenQueryKey<FirstArg<TCall>, TMethod>>;
|
|
59
|
-
queryOptions: (input?: FirstArg<TCall>, options?: EdenQueryOptions<NarrowedData<EdenData<TCall>, TThrowOnError>, EdenFetchError | Error, NarrowedData<EdenData<TCall>, TThrowOnError>, EdenQueryKey<FirstArg<TCall>, TMethod>, EdenPartial<NonNullable<FirstArg<TCall>>>>) =>
|
|
60
|
-
queryKey: EdenQueryKey<FirstArg<TCall>, TMethod>;
|
|
61
|
-
queryFn: (context: QueryFunctionContext<EdenQueryKey<FirstArg<TCall>, TMethod>>) => Promise<NarrowedData<EdenData<TCall>, TThrowOnError>>;
|
|
62
|
-
};
|
|
76
|
+
queryOptions: (input?: FirstArg<TCall>, options?: EdenQueryOptions<NarrowedData<EdenData<TCall>, TThrowOnError>, EdenFetchError | Error, NarrowedData<EdenData<TCall>, TThrowOnError>, EdenQueryKey<FirstArg<TCall>, TMethod>, EdenPartial<NonNullable<FirstArg<TCall>>>>) => EdenQueryOptionsResult<NarrowedData<EdenData<TCall>, TThrowOnError>, EdenFetchError | Error, NarrowedData<EdenData<TCall>, TThrowOnError>, EdenQueryKey<FirstArg<TCall>, TMethod>>;
|
|
63
77
|
};
|
|
64
78
|
type EdenMutationMethod<TMethod extends Extract<HTTPMethod, 'post' | 'put' | 'patch' | 'delete'>, TCall extends (...args: any[]) => Promise<any>, TThrowOnError extends ThrowOnErrorOption = true> = TCall & {
|
|
65
79
|
mutationOptions: (options?: EdenMutationOptions<NarrowedData<EdenData<TCall>, TThrowOnError>, EdenFetchError | Error, FirstArg<TCall>, unknown, EdenPartial<NonNullable<SecondArg<TCall>>>>) => EdenMutationOptions<NarrowedData<EdenData<TCall>, TThrowOnError>, EdenFetchError | Error, FirstArg<TCall>, unknown, EdenPartial<NonNullable<SecondArg<TCall>>>> & {
|
|
@@ -95,4 +109,4 @@ declare function createEdenQuery<App extends Elysia<any, any, any, any, any, any
|
|
|
95
109
|
treaty?: Treaty.Config;
|
|
96
110
|
}): EdenQueryify<Treaty.Create<App>, TThrowOnError>;
|
|
97
111
|
|
|
98
|
-
export { type EdenErrorContext, type EdenInfiniteQueryOptions, type EdenMutationMethod, type EdenMutationOptions, type EdenOptions, type EdenQueryConfig, type EdenQueryKey, type EdenQueryMethod, type EdenQueryOptions, type EdenQueryify, type EdenThrowOnError, type EdenThrowOnErrorContext, type HTTPMethod, type NarrowedData, type ThrowOnErrorOption, createEdenQuery };
|
|
112
|
+
export { type EdenErrorContext, type EdenInfiniteQueryOptions, type EdenMutationMethod, type EdenMutationOptions, type EdenOptions, type EdenQueryConfig, type EdenQueryKey, type EdenQueryMethod, type EdenQueryOptions, type EdenQueryOptionsResult, type EdenQueryify, type EdenThrowOnError, type EdenThrowOnErrorContext, type HTTPMethod, type NarrowedData, type ThrowOnErrorOption, createEdenQuery };
|
package/dist/index.js
CHANGED
|
@@ -35,7 +35,12 @@ function createQueryOptions(treatyCall, paths, method, globalConfig) {
|
|
|
35
35
|
return (input, options) => {
|
|
36
36
|
const eden = options?.eden;
|
|
37
37
|
const finalInput = input === void 0 && eden === void 0 ? void 0 : { ...input, ...eden };
|
|
38
|
-
const queryKey = buildQueryKey(
|
|
38
|
+
const queryKey = buildQueryKey(
|
|
39
|
+
paths,
|
|
40
|
+
finalInput,
|
|
41
|
+
method,
|
|
42
|
+
globalConfig.queryKeyPrefix
|
|
43
|
+
);
|
|
39
44
|
const { eden: _eden, ...tanstackOptions } = options ?? {};
|
|
40
45
|
return {
|
|
41
46
|
...tanstackOptions,
|
|
@@ -157,7 +162,7 @@ function createQueryFilter(paths, method, prefix) {
|
|
|
157
162
|
// src/proxy.ts
|
|
158
163
|
var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
159
164
|
function extendProxy(proxy, paths, config) {
|
|
160
|
-
|
|
165
|
+
const handler = {
|
|
161
166
|
get(target, prop) {
|
|
162
167
|
if (typeof prop === "symbol") return target[prop];
|
|
163
168
|
const value = target[prop];
|
|
@@ -189,8 +194,16 @@ function extendProxy(proxy, paths, config) {
|
|
|
189
194
|
return extendProxy(value, [...paths, prop], config);
|
|
190
195
|
}
|
|
191
196
|
return value;
|
|
197
|
+
},
|
|
198
|
+
apply(target, thisArg, args) {
|
|
199
|
+
const result = Reflect.apply(target, thisArg, args);
|
|
200
|
+
if (result !== null && (typeof result === "object" || typeof result === "function")) {
|
|
201
|
+
return extendProxy(result, paths, config);
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
192
204
|
}
|
|
193
|
-
}
|
|
205
|
+
};
|
|
206
|
+
return new Proxy(proxy, handler);
|
|
194
207
|
}
|
|
195
208
|
|
|
196
209
|
// src/index.ts
|