@sylphx/lens-react 2.5.13 → 2.5.15

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/dist/index.d.ts CHANGED
@@ -141,284 +141,4 @@ type TypedClient<TRouter extends RouterDef> = TRouter extends RouterDef<infer TR
141
141
  declare function createClient<TRouter extends RouterDef>(config: LensClientConfig | TypedClientConfig<{
142
142
  router: TRouter;
143
143
  }>): TypedClient<TRouter>;
144
- import { LensClient } from "@sylphx/lens-client";
145
- import { ReactElement, ReactNode } from "react";
146
- interface LensProviderProps {
147
- /** Lens client instance */
148
- client: LensClient<any, any>;
149
- /** Children */
150
- children: ReactNode;
151
- }
152
- /**
153
- * Provides Lens client to component tree
154
- *
155
- * @example
156
- * ```tsx
157
- * import { createClient, http } from '@sylphx/lens-client';
158
- * import { LensProvider } from '@sylphx/lens-react';
159
- * import type { AppRouter } from './server';
160
- *
161
- * const client = createClient<AppRouter>({
162
- * transport: http({ url: '/api' }),
163
- * });
164
- *
165
- * function App() {
166
- * return (
167
- * <LensProvider client={client}>
168
- * <UserProfile />
169
- * </LensProvider>
170
- * );
171
- * }
172
- * ```
173
- */
174
- declare function LensProvider({ client, children }: LensProviderProps): ReactElement;
175
- /**
176
- * Get Lens client from context
177
- *
178
- * @throws Error if used outside LensProvider
179
- *
180
- * @example
181
- * ```tsx
182
- * function UserProfile({ userId }: { userId: string }) {
183
- * const client = useLensClient<AppRouter>();
184
- * const { data } = useQuery(client.user.get({ id: userId }));
185
- * return <h1>{data?.name}</h1>;
186
- * }
187
- * ```
188
- */
189
- declare function useLensClient<TRouter = any>(): LensClient<any, any> & TRouter;
190
- import { LensClient as LensClient2, MutationResult, QueryResult as QueryResult2 } from "@sylphx/lens-client";
191
- import { DependencyList } from "react";
192
- /** Result of useQuery hook */
193
- interface UseQueryResult<T> {
194
- /** Query data (null if loading or error) */
195
- data: T | null;
196
- /** Loading state */
197
- loading: boolean;
198
- /** Error state */
199
- error: Error | null;
200
- /** Refetch the query */
201
- refetch: () => void;
202
- }
203
- /** Result of useMutation hook */
204
- interface UseMutationResult<
205
- TInput,
206
- TOutput
207
- > {
208
- /** Execute the mutation */
209
- mutate: (input: TInput) => Promise<MutationResult<TOutput>>;
210
- /** Mutation is in progress */
211
- loading: boolean;
212
- /** Mutation error */
213
- error: Error | null;
214
- /** Last mutation result */
215
- data: TOutput | null;
216
- /** Reset mutation state */
217
- reset: () => void;
218
- }
219
- /** Options for useQuery */
220
- interface UseQueryOptions<
221
- TData = unknown,
222
- TSelected = TData
223
- > {
224
- /** Skip the query (don't execute) */
225
- skip?: boolean;
226
- /** Transform the query result */
227
- select?: (data: TData) => TSelected;
228
- }
229
- /** Client type for callbacks */
230
- type Client = LensClient2<any, any>;
231
- /** Route selector - callback that returns a route function */
232
- type RouteSelector<
233
- TParams,
234
- TResult
235
- > = (client: Client) => ((params: TParams) => QueryResult2<TResult>) | null;
236
- /** Query accessor selector - callback that returns QueryResult */
237
- type QuerySelector<TResult> = (client: Client) => QueryResult2<TResult> | null | undefined;
238
- /** Mutation selector - callback that returns mutation function */
239
- type MutationSelector<
240
- TInput,
241
- TOutput
242
- > = (client: Client) => (input: TInput) => Promise<MutationResult<TOutput>>;
243
- /**
244
- * Subscribe to a query with reactive updates.
245
- * Client is automatically injected from LensProvider context.
246
- *
247
- * Two usage patterns:
248
- *
249
- * **1. Route + Params (recommended)** - Stable references, no infinite loops
250
- * ```tsx
251
- * const { data } = useQuery((client) => client.user.get, { id: userId });
252
- * ```
253
- *
254
- * **2. Accessor + Deps (escape hatch)** - For complex/composed queries
255
- * ```tsx
256
- * const { data } = useQuery((client) => client.user.get({ id }), [id]);
257
- * ```
258
- *
259
- * @example
260
- * ```tsx
261
- * // Basic usage - Route + Params
262
- * function UserProfile({ userId }: { userId: string }) {
263
- * const { data: user, loading, error } = useQuery(
264
- * (client) => client.user.get,
265
- * { id: userId }
266
- * );
267
- *
268
- * if (loading) return <Spinner />;
269
- * if (error) return <Error message={error.message} />;
270
- * return <h1>{user?.name}</h1>;
271
- * }
272
- *
273
- * // With select transform
274
- * function UserName({ userId }: { userId: string }) {
275
- * const { data: name } = useQuery(
276
- * (client) => client.user.get,
277
- * { id: userId },
278
- * { select: (user) => user.name }
279
- * );
280
- * return <span>{name}</span>;
281
- * }
282
- *
283
- * // Conditional query (return null to skip)
284
- * function SessionInfo({ sessionId }: { sessionId: string | null }) {
285
- * const { data } = useQuery(
286
- * (client) => sessionId ? client.session.get : null,
287
- * { id: sessionId ?? '' }
288
- * );
289
- * return <span>{data?.totalTokens}</span>;
290
- * }
291
- *
292
- * // Skip query with option
293
- * function ConditionalQuery({ userId, shouldFetch }: { userId: string; shouldFetch: boolean }) {
294
- * const { data } = useQuery(
295
- * (client) => client.user.get,
296
- * { id: userId },
297
- * { skip: !shouldFetch }
298
- * );
299
- * }
300
- *
301
- * // Complex queries with accessor (escape hatch)
302
- * function ComplexQuery({ userId }: { userId: string }) {
303
- * const { data } = useQuery(
304
- * (client) => client.user.get({ id: userId }),
305
- * [userId]
306
- * );
307
- * }
308
- * ```
309
- */
310
- declare function useQuery2<
311
- TParams,
312
- TResult,
313
- TSelected = TResult
314
- >(selector: RouteSelector<TParams, TResult>, params: TParams, options?: UseQueryOptions<TResult, TSelected>): UseQueryResult<TSelected>;
315
- declare function useQuery2<
316
- TResult,
317
- TSelected = TResult
318
- >(selector: QuerySelector<TResult>, deps: DependencyList, options?: UseQueryOptions<TResult, TSelected>): UseQueryResult<TSelected>;
319
- /**
320
- * Execute mutations with loading/error state.
321
- * Client is automatically injected from LensProvider context.
322
- *
323
- * @param selector - Callback that returns mutation function from client
324
- *
325
- * @example
326
- * \`\`\`tsx
327
- * function CreatePost() {
328
- * const { mutate, loading, error, data } = useMutation(
329
- * (client) => client.post.create
330
- * );
331
- *
332
- * const handleSubmit = async (formData: FormData) => {
333
- * try {
334
- * const result = await mutate({
335
- * title: formData.get('title'),
336
- * content: formData.get('content'),
337
- * });
338
- * console.log('Created:', result.data);
339
- * } catch (err) {
340
- * console.error('Failed:', err);
341
- * }
342
- * };
343
- *
344
- * return (
345
- * <form onSubmit={handleSubmit}>
346
- * <button type="submit" disabled={loading}>
347
- * {loading ? 'Creating...' : 'Create'}
348
- * </button>
349
- * {error && <p className="error">{error.message}</p>}
350
- * </form>
351
- * );
352
- * }
353
- *
354
- * // With optimistic updates
355
- * function UpdatePost({ postId }: { postId: string }) {
356
- * const { mutate } = useMutation((client) => client.post.update);
357
- *
358
- * const handleUpdate = async (title: string) => {
359
- * const result = await mutate({ id: postId, title });
360
- * // result.rollback?.() can undo optimistic update
361
- * };
362
- * }
363
- * \`\`\`
364
- */
365
- declare function useMutation2<
366
- TInput,
367
- TOutput
368
- >(selector: MutationSelector<TInput, TOutput>): UseMutationResult<TInput, TOutput>;
369
- /** Result of useLazyQuery hook */
370
- interface UseLazyQueryResult<T> {
371
- /** Execute the query */
372
- execute: () => Promise<T>;
373
- /** Query data (null if not executed or error) */
374
- data: T | null;
375
- /** Loading state */
376
- loading: boolean;
377
- /** Error state */
378
- error: Error | null;
379
- /** Reset query state */
380
- reset: () => void;
381
- }
382
- /**
383
- * Execute a query on demand (not on mount).
384
- * Client is automatically injected from LensProvider context.
385
- *
386
- * @example
387
- * ```tsx
388
- * // Route + Params pattern
389
- * function SearchUsers() {
390
- * const [searchTerm, setSearchTerm] = useState('');
391
- * const { execute, data, loading } = useLazyQuery(
392
- * (client) => client.user.search,
393
- * { query: searchTerm }
394
- * );
395
- *
396
- * return (
397
- * <div>
398
- * <input value={searchTerm} onChange={e => setSearchTerm(e.target.value)} />
399
- * <button onClick={execute} disabled={loading}>Search</button>
400
- * {data?.map(user => <UserCard key={user.id} user={user} />)}
401
- * </div>
402
- * );
403
- * }
404
- *
405
- * // Accessor pattern
406
- * function LazyComplexQuery({ userId }: { userId: string }) {
407
- * const { execute, data } = useLazyQuery(
408
- * (client) => client.user.get({ id: userId }),
409
- * [userId]
410
- * );
411
- * return <button onClick={execute}>Load</button>;
412
- * }
413
- * ```
414
- */
415
- declare function useLazyQuery<
416
- TParams,
417
- TResult,
418
- TSelected = TResult
419
- >(selector: RouteSelector<TParams, TResult>, params: TParams, options?: UseQueryOptions<TResult, TSelected>): UseLazyQueryResult<TSelected>;
420
- declare function useLazyQuery<
421
- TResult,
422
- TSelected = TResult
423
- >(selector: QuerySelector<TResult>, deps: DependencyList, options?: UseQueryOptions<TResult, TSelected>): UseLazyQueryResult<TSelected>;
424
- export { useQuery2 as useQuery, useMutation2 as useMutation, useLensClient, useLazyQuery, createClient, UseQueryResult, UseQueryOptions, UseMutationResult, UseLazyQueryResult, TypedClient, RouteSelector, QuerySelector, QueryHookResult, QueryHookOptions, QueryEndpoint, MutationSelector, MutationHookResult, MutationHookOptions, MutationEndpoint, LensProviderProps, LensProvider };
144
+ export { createClient, TypedClient, QueryHookResult, QueryHookOptions, QueryEndpoint, MutationHookResult, MutationHookOptions, MutationEndpoint };
package/dist/index.js CHANGED
@@ -206,256 +206,6 @@ function createClient(config) {
206
206
  }
207
207
  return createProxy("");
208
208
  }
209
- // src/context.tsx
210
- import { createContext, useContext } from "react";
211
- import { jsx } from "react/jsx-runtime";
212
- var LENS_CONTEXT_KEY = Symbol.for("@sylphx/lens-react/context");
213
- function getOrCreateContext() {
214
- const globalObj = globalThis;
215
- if (!globalObj[LENS_CONTEXT_KEY]) {
216
- globalObj[LENS_CONTEXT_KEY] = createContext(null);
217
- }
218
- return globalObj[LENS_CONTEXT_KEY];
219
- }
220
- var LensContext = getOrCreateContext();
221
- function LensProvider({ client, children }) {
222
- return /* @__PURE__ */ jsx(LensContext.Provider, {
223
- value: client,
224
- children
225
- });
226
- }
227
- function useLensClient() {
228
- const client = useContext(LensContext);
229
- if (!client) {
230
- throw new Error("useLensClient must be used within a <LensProvider>. " + "Make sure to wrap your app with <LensProvider client={client}>.");
231
- }
232
- return client;
233
- }
234
- // src/hooks.ts
235
- import {
236
- useCallback as useCallback2,
237
- useEffect as useEffect2,
238
- useMemo as useMemo2,
239
- useReducer as useReducer2,
240
- useRef as useRef2,
241
- useState as useState2
242
- } from "react";
243
- function queryReducer2(state, action) {
244
- switch (action.type) {
245
- case "RESET":
246
- return { data: null, loading: false, error: null };
247
- case "START":
248
- return { ...state, loading: true, error: null };
249
- case "SUCCESS":
250
- return { data: action.data, loading: false, error: null };
251
- case "ERROR":
252
- return { ...state, loading: false, error: action.error };
253
- case "LOADING_DONE":
254
- return { ...state, loading: false };
255
- default:
256
- return state;
257
- }
258
- }
259
- function useQuery(selector, paramsOrDeps, options) {
260
- const client = useLensClient();
261
- const isAccessorMode = Array.isArray(paramsOrDeps);
262
- const paramsKey = !isAccessorMode ? JSON.stringify(paramsOrDeps) : null;
263
- const selectorRef = useRef2(selector);
264
- selectorRef.current = selector;
265
- const query = useMemo2(() => {
266
- if (options?.skip)
267
- return null;
268
- if (isAccessorMode) {
269
- const querySelector = selectorRef.current;
270
- return querySelector(client);
271
- }
272
- const routeSelector = selectorRef.current;
273
- const route = routeSelector(client);
274
- if (!route)
275
- return null;
276
- return route(paramsOrDeps);
277
- }, isAccessorMode ? [client, options?.skip, ...paramsOrDeps] : [client, paramsKey, options?.skip]);
278
- const selectRef = useRef2(options?.select);
279
- selectRef.current = options?.select;
280
- const initialState = {
281
- data: null,
282
- loading: query != null && !options?.skip,
283
- error: null
284
- };
285
- const [state, dispatch] = useReducer2(queryReducer2, initialState);
286
- const mountedRef = useRef2(true);
287
- const queryRef = useRef2(query);
288
- queryRef.current = query;
289
- const transform = useCallback2((value) => {
290
- return selectRef.current ? selectRef.current(value) : value;
291
- }, []);
292
- useEffect2(() => {
293
- mountedRef.current = true;
294
- if (query == null) {
295
- dispatch({ type: "RESET" });
296
- return;
297
- }
298
- dispatch({ type: "START" });
299
- let hasReceivedData = false;
300
- const unsubscribe = query.subscribe((value) => {
301
- if (mountedRef.current) {
302
- hasReceivedData = true;
303
- dispatch({ type: "SUCCESS", data: transform(value) });
304
- }
305
- });
306
- query.then((value) => {
307
- if (mountedRef.current) {
308
- if (!hasReceivedData) {
309
- dispatch({ type: "SUCCESS", data: transform(value) });
310
- } else {
311
- dispatch({ type: "LOADING_DONE" });
312
- }
313
- }
314
- }, (err) => {
315
- if (mountedRef.current) {
316
- dispatch({
317
- type: "ERROR",
318
- error: err instanceof Error ? err : new Error(String(err))
319
- });
320
- }
321
- });
322
- return () => {
323
- mountedRef.current = false;
324
- unsubscribe();
325
- };
326
- }, [query, transform]);
327
- const refetch = useCallback2(() => {
328
- const currentQuery = queryRef.current;
329
- if (currentQuery == null)
330
- return;
331
- dispatch({ type: "START" });
332
- currentQuery.then((value) => {
333
- if (mountedRef.current) {
334
- dispatch({ type: "SUCCESS", data: transform(value) });
335
- }
336
- }, (err) => {
337
- if (mountedRef.current) {
338
- dispatch({
339
- type: "ERROR",
340
- error: err instanceof Error ? err : new Error(String(err))
341
- });
342
- }
343
- });
344
- }, [transform]);
345
- return { data: state.data, loading: state.loading, error: state.error, refetch };
346
- }
347
- function useMutation(selector) {
348
- const client = useLensClient();
349
- const mutationFn = selector(client);
350
- const [loading, setLoading] = useState2(false);
351
- const [error, setError] = useState2(null);
352
- const [data, setData] = useState2(null);
353
- const mountedRef = useRef2(true);
354
- const mutationRef = useRef2(mutationFn);
355
- mutationRef.current = mutationFn;
356
- useEffect2(() => {
357
- mountedRef.current = true;
358
- return () => {
359
- mountedRef.current = false;
360
- };
361
- }, []);
362
- const mutate = useCallback2(async (input) => {
363
- setLoading(true);
364
- setError(null);
365
- try {
366
- const result = await mutationRef.current(input);
367
- if (mountedRef.current) {
368
- setData(result.data);
369
- }
370
- return result;
371
- } catch (err) {
372
- const mutationError = err instanceof Error ? err : new Error(String(err));
373
- if (mountedRef.current) {
374
- setError(mutationError);
375
- }
376
- throw mutationError;
377
- } finally {
378
- if (mountedRef.current) {
379
- setLoading(false);
380
- }
381
- }
382
- }, []);
383
- const reset = useCallback2(() => {
384
- setLoading(false);
385
- setError(null);
386
- setData(null);
387
- }, []);
388
- return { mutate, loading, error, data, reset };
389
- }
390
- function useLazyQuery(selector, paramsOrDeps, options) {
391
- const client = useLensClient();
392
- const [data, setData] = useState2(null);
393
- const [loading, setLoading] = useState2(false);
394
- const [error, setError] = useState2(null);
395
- const mountedRef = useRef2(true);
396
- const isAccessorMode = Array.isArray(paramsOrDeps);
397
- const selectorRef = useRef2(selector);
398
- selectorRef.current = selector;
399
- const paramsOrDepsRef = useRef2(paramsOrDeps);
400
- paramsOrDepsRef.current = paramsOrDeps;
401
- const selectRef = useRef2(options?.select);
402
- selectRef.current = options?.select;
403
- useEffect2(() => {
404
- mountedRef.current = true;
405
- return () => {
406
- mountedRef.current = false;
407
- };
408
- }, []);
409
- const execute = useCallback2(async () => {
410
- let query;
411
- if (isAccessorMode) {
412
- const querySelector = selectorRef.current;
413
- query = querySelector(client);
414
- } else {
415
- const routeSelector = selectorRef.current;
416
- const route = routeSelector(client);
417
- if (route) {
418
- query = route(paramsOrDepsRef.current);
419
- }
420
- }
421
- if (query == null) {
422
- setData(null);
423
- setLoading(false);
424
- return null;
425
- }
426
- setLoading(true);
427
- setError(null);
428
- try {
429
- const result = await query;
430
- const selected = selectRef.current ? selectRef.current(result) : result;
431
- if (mountedRef.current) {
432
- setData(selected);
433
- }
434
- return selected;
435
- } catch (err) {
436
- const queryError = err instanceof Error ? err : new Error(String(err));
437
- if (mountedRef.current) {
438
- setError(queryError);
439
- }
440
- throw queryError;
441
- } finally {
442
- if (mountedRef.current) {
443
- setLoading(false);
444
- }
445
- }
446
- }, [client, isAccessorMode]);
447
- const reset = useCallback2(() => {
448
- setLoading(false);
449
- setError(null);
450
- setData(null);
451
- }, []);
452
- return { execute, data, loading, error, reset };
453
- }
454
209
  export {
455
- useQuery,
456
- useMutation,
457
- useLensClient,
458
- useLazyQuery,
459
- createClient,
460
- LensProvider
210
+ createClient
461
211
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylphx/lens-react",
3
- "version": "2.5.13",
3
+ "version": "2.5.15",
4
4
  "description": "React bindings for Lens API framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -30,8 +30,8 @@
30
30
  "author": "SylphxAI",
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
- "@sylphx/lens-client": "^2.7.8",
34
- "@sylphx/lens-core": "^3.0.1"
33
+ "@sylphx/lens-client": "^3.0.1",
34
+ "@sylphx/lens-core": "^4.0.2"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "react": ">=18.0.0"
package/src/index.ts CHANGED
@@ -25,10 +25,6 @@
25
25
  * ```
26
26
  */
27
27
 
28
- // =============================================================================
29
- // New API (v4) - Recommended
30
- // =============================================================================
31
-
32
28
  export {
33
29
  createClient,
34
30
  type MutationEndpoint,
@@ -39,25 +35,3 @@ export {
39
35
  type QueryHookResult,
40
36
  type TypedClient,
41
37
  } from "./create.js";
42
-
43
- // =============================================================================
44
- // Legacy API (v3) - Deprecated, will be removed in v3.0
45
- // =============================================================================
46
-
47
- export { LensProvider, type LensProviderProps, useLensClient } from "./context.js";
48
-
49
- export {
50
- // Types
51
- type MutationSelector,
52
- type QuerySelector,
53
- type RouteSelector,
54
- type UseLazyQueryResult,
55
- type UseMutationResult,
56
- type UseQueryOptions,
57
- type UseQueryResult,
58
- // Query hooks
59
- useLazyQuery,
60
- // Mutation hook
61
- useMutation,
62
- useQuery,
63
- } from "./hooks.js";