eden-tanstack-query 0.0.7 → 0.0.9

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 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
- // Default: throwOnError = true
169
- // Data type in select/onSuccess excludes { error: string } shapes
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) => data.users, // TypeScript knows data doesn't have error shape
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
- console.log(data.users) // No need for type guards
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` or a function, the full union type is preserved:
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
- // Need to handle both success and error shapes
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
@@ -45,7 +45,20 @@ interface EdenOptions<TEden = unknown> {
45
45
  eden?: TEden;
46
46
  }
47
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
+ */
48
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'>;
49
62
  type EdenMutationOptions<TData, TError = Error, TVariables = void, TContext = unknown, TEden = unknown> = Omit<MutationOptions<TData, TError, TVariables, TContext>, 'mutationFn'> & EdenOptions<TEden>;
50
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>;
51
64
  type EdenPartial<T> = T extends object ? Partial<T> : never;
@@ -60,10 +73,7 @@ type EdenData<T> = Extract<AwaitedReturn<T>, {
60
73
  type EdenQueryMethod<TMethod extends Extract<HTTPMethod, 'get' | 'head'>, TCall extends (...args: any[]) => Promise<any>, TThrowOnError extends ThrowOnErrorOption = true> = TCall & {
61
74
  queryKey: (input?: FirstArg<TCall>) => EdenQueryKey<FirstArg<TCall>, TMethod>;
62
75
  queryFilter: (input?: FirstArg<TCall>) => QueryFilters<EdenQueryKey<FirstArg<TCall>, TMethod>>;
63
- queryOptions: (input?: FirstArg<TCall>, options?: EdenQueryOptions<NarrowedData<EdenData<TCall>, TThrowOnError>, EdenFetchError | Error, NarrowedData<EdenData<TCall>, TThrowOnError>, EdenQueryKey<FirstArg<TCall>, TMethod>, EdenPartial<NonNullable<FirstArg<TCall>>>>) => EdenQueryOptions<NarrowedData<EdenData<TCall>, TThrowOnError>, EdenFetchError | Error, NarrowedData<EdenData<TCall>, TThrowOnError>, EdenQueryKey<FirstArg<TCall>, TMethod>, EdenPartial<NonNullable<FirstArg<TCall>>>> & {
64
- queryKey: EdenQueryKey<FirstArg<TCall>, TMethod>;
65
- queryFn: (context: QueryFunctionContext<EdenQueryKey<FirstArg<TCall>, TMethod>>) => Promise<NarrowedData<EdenData<TCall>, TThrowOnError>>;
66
- };
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>>;
67
77
  };
68
78
  type EdenMutationMethod<TMethod extends Extract<HTTPMethod, 'post' | 'put' | 'patch' | 'delete'>, TCall extends (...args: any[]) => Promise<any>, TThrowOnError extends ThrowOnErrorOption = true> = TCall & {
69
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>>>> & {
@@ -99,4 +109,4 @@ declare function createEdenQuery<App extends Elysia<any, any, any, any, any, any
99
109
  treaty?: Treaty.Config;
100
110
  }): EdenQueryify<Treaty.Create<App>, TThrowOnError>;
101
111
 
102
- 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(paths, finalInput, method, globalConfig.queryKeyPrefix);
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
- return new Proxy(proxy, {
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eden-tanstack-query",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "TanStack Query integration for Eden Treaty",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",