@vladimirdev635/gql-client-react 0.0.41 → 0.0.42

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vladimirdev635/gql-client-react",
3
- "version": "0.0.41",
3
+ "version": "0.0.42",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ import { useSubscription } from '../useSubscription.jsx';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { renderHook, act } from '@testing-library/react';
4
+ import assert from 'assert';
5
+ import { testSubscription } from './utils.js';
6
+ describe('useSubscription', () => {
7
+ it('Should return loading state and then success state', async () => {
8
+ const executor = async () => {
9
+ const readableStream = new ReadableStream();
10
+ const response = new Response(readableStream);
11
+ return {
12
+ result: (async function* () {
13
+ yield { number: 1 };
14
+ yield { number: 2 };
15
+ yield { number: 3 };
16
+ })(),
17
+ response
18
+ };
19
+ };
20
+ const { result } = renderHook((props) => useSubscription(...props), {
21
+ initialProps: [executor, testSubscription, {}, {}]
22
+ });
23
+ expect(result.current.state).toBe('loading');
24
+ act(() => { });
25
+ await act(async () => { });
26
+ expect(result.current.state).toBe('success');
27
+ assert(result.current.state === 'success');
28
+ let number = 1;
29
+ for await (const value of result.current.result) {
30
+ expect(value.number).toBe(number);
31
+ number++;
32
+ }
33
+ });
34
+ it('Should return loading state and then failure state', async () => {
35
+ const error = new Error('Network error');
36
+ const executor = async () => { throw error; };
37
+ const { result } = renderHook((props) => useSubscription(...props), {
38
+ initialProps: [executor, testSubscription, {}, {}]
39
+ });
40
+ expect(result.current.state).toBe('loading');
41
+ act(() => { });
42
+ await act(async () => { });
43
+ expect(result.current.state).toBe('failure');
44
+ assert(result.current.state === 'failure');
45
+ expect(result.current.error).toBe(error);
46
+ });
47
+ });
package/tests/utils.d.ts CHANGED
@@ -7,3 +7,14 @@ export declare const testOperation: {
7
7
  readonly resultSchema: z.ZodObject<{}, z.core.$strip>;
8
8
  };
9
9
  export type TestOperationResult = z.infer<(typeof testOperation)['resultSchema']>;
10
+ export declare const testSubscription: {
11
+ readonly document: "";
12
+ readonly name: "";
13
+ readonly type: "SUBSCRIPTION";
14
+ readonly variablesSchema: z.ZodObject<{}, z.core.$strip>;
15
+ readonly resultSchema: z.ZodObject<{
16
+ number: z.ZodNumber;
17
+ }, z.core.$strip>;
18
+ };
19
+ export type TestSubscriptionResult = z.infer<(typeof testSubscription)['resultSchema']>;
20
+ export declare function asyncTimeout(timeoutMS: number): Promise<void>;
package/tests/utils.js CHANGED
@@ -6,3 +6,13 @@ export const testOperation = {
6
6
  variablesSchema: z.object({}),
7
7
  resultSchema: z.object({})
8
8
  };
9
+ export const testSubscription = {
10
+ document: '',
11
+ name: '',
12
+ type: 'SUBSCRIPTION',
13
+ variablesSchema: z.object({}),
14
+ resultSchema: z.object({ number: z.number() })
15
+ };
16
+ export async function asyncTimeout(timeoutMS) {
17
+ return await new Promise(resolve => setTimeout(resolve, timeoutMS));
18
+ }
package/types.d.ts CHANGED
@@ -1,16 +1,30 @@
1
1
  import { z } from 'zod/v4';
2
- export interface Operation {
2
+ export interface SyncOperation {
3
3
  name: string;
4
4
  document: string;
5
- type: 'QUERY' | 'MUTATION' | 'SUBSCRIPTION';
5
+ type: 'QUERY' | 'MUTATION';
6
6
  variablesSchema: z.ZodType;
7
7
  resultSchema: z.ZodType;
8
8
  }
9
+ export interface SubscriptionOperation {
10
+ name: string;
11
+ document: string;
12
+ type: 'SUBSCRIPTION';
13
+ variablesSchema: z.ZodType;
14
+ resultSchema: z.ZodType;
15
+ }
16
+ export type Operation = SyncOperation | SubscriptionOperation;
9
17
  export interface ExecuteResult<TResult> {
10
18
  result: TResult;
11
19
  response: Response;
12
20
  }
21
+ export type OperationResult<T extends Operation> = z.infer<T['resultSchema']>;
22
+ export type OperationVariables<T extends Operation> = z.infer<T['variablesSchema']>;
23
+ export type SubOpAsyncIterable<T extends Operation> = AsyncIterable<OperationResult<T>>;
13
24
  export interface RequestContext {
14
25
  fetchOptions?: RequestInit;
15
26
  }
16
- export type Executor<TRequestContext extends RequestContext> = <T extends Operation>(operation: T, variables: z.infer<T['variablesSchema']>, context: TRequestContext) => Promise<ExecuteResult<z.infer<T['resultSchema']>>>;
27
+ export type Executor<TRequestContext extends RequestContext> = {
28
+ <TSyncOp extends SyncOperation>(operation: TSyncOp, variables: OperationVariables<TSyncOp>, context: TRequestContext): Promise<ExecuteResult<OperationResult<TSyncOp>>>;
29
+ <TOperation extends SubscriptionOperation>(operation: TOperation, variables: OperationVariables<TOperation>, context: TRequestContext): Promise<ExecuteResult<SubOpAsyncIterable<TOperation>>>;
30
+ };
@@ -1,5 +1,4 @@
1
- import { z } from 'zod/v4';
2
- import { Executor, Operation, RequestContext } from './types.js';
1
+ import type { Executor, OperationResult, OperationVariables, RequestContext, SyncOperation } from './types.js';
3
2
  import { type OperationFailureState, type OperationState, type OperationSuccessState } from './useOperation.jsx';
4
3
  export interface LazyOperationInitialState {
5
4
  state: 'initial';
@@ -14,5 +13,5 @@ export interface UseLazyOperationReturnType<TVariables, TResult, TRequestContext
14
13
  state: LazyOperationState<TResult>;
15
14
  reset: () => void;
16
15
  }
17
- export declare function useLazyOperation<T extends Operation, TRequestContext extends RequestContext>(executor: Executor<TRequestContext>, operation: T): UseLazyOperationReturnType<z.infer<T['variablesSchema']>, z.infer<T['resultSchema']>, TRequestContext>;
16
+ export declare function useLazyOperation<T extends SyncOperation, TRequestContext extends RequestContext>(executor: Executor<TRequestContext>, operation: T): UseLazyOperationReturnType<OperationVariables<T>, OperationResult<T>, TRequestContext>;
18
17
  export {};
@@ -1,5 +1,5 @@
1
- import { loadingState } from './useOperation.jsx';
2
1
  import { useCallback, useState } from 'react';
2
+ import { loadingState } from './useOperation.jsx';
3
3
  export const lazyInitialState = Object.freeze({ state: 'initial' });
4
4
  async function execute(executor, operation, variables, requestContext, setState) {
5
5
  let newState;
@@ -20,8 +20,8 @@ export function useLazyOperation(executor, operation) {
20
20
  return execute(executor, operation, variables, requestContext, setState);
21
21
  }, [setState, executor, operation]);
22
22
  return {
23
- execute: executeCallback,
24
23
  state,
24
+ execute: executeCallback,
25
25
  reset: () => setState(lazyInitialState)
26
26
  };
27
27
  }
package/useOperation.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { z } from 'zod/v4';
2
- import { ExecuteResult, Executor, Operation, RequestContext } from './types.js';
1
+ import type { ExecuteResult, Executor, OperationResult, OperationVariables, RequestContext, SyncOperation } from './types.js';
3
2
  export interface OperationLoadingState {
4
3
  state: 'loading';
5
4
  }
@@ -14,4 +13,4 @@ export type OperationState<TResult> = OperationLoadingState | OperationSuccessSt
14
13
  export declare const loadingState: Readonly<{
15
14
  readonly state: "loading";
16
15
  }>;
17
- export declare function useOperation<T extends Operation, TRequestContext extends RequestContext>(executor: Executor<TRequestContext>, operation: T, variables: z.infer<T['variablesSchema']>, requestContext: TRequestContext): OperationState<z.infer<T['resultSchema']>>;
16
+ export declare function useOperation<T extends SyncOperation, TRequestContext extends RequestContext>(executor: Executor<TRequestContext>, operation: T, variables: OperationVariables<T>, requestContext: TRequestContext): OperationState<OperationResult<T>>;
@@ -0,0 +1,3 @@
1
+ import type { Executor, OperationVariables, RequestContext, SubOpAsyncIterable, SubscriptionOperation } from './types.js';
2
+ import { type OperationState } from './useOperation.jsx';
3
+ export declare function useSubscription<T extends SubscriptionOperation, TRequestContext extends RequestContext>(executor: Executor<TRequestContext>, operation: T, variables: OperationVariables<T>, requestContext: TRequestContext): OperationState<SubOpAsyncIterable<T>>;
@@ -0,0 +1,21 @@
1
+ import { useEffect, useMemo, useState } from 'react';
2
+ import hash from 'object-hash';
3
+ import { loadingState } from './useOperation.jsx';
4
+ export function useSubscription(executor, operation, variables, requestContext) {
5
+ const [state, setState] = useState(loadingState);
6
+ const memoizedVariables = useMemo(() => variables, [hash(variables)]);
7
+ const memoizedRequestContext = useMemo(() => requestContext, [hash(requestContext)]);
8
+ useEffect(() => {
9
+ executor(operation, variables, requestContext)
10
+ .then(result => setState({ state: 'success', ...result }))
11
+ .catch(error => setState({ state: 'failure', error }));
12
+ return () => setState(loadingState);
13
+ }, [
14
+ setState,
15
+ executor,
16
+ operation,
17
+ memoizedVariables,
18
+ memoizedRequestContext
19
+ ]);
20
+ return state;
21
+ }