@spoosh/react 0.7.0 → 0.7.2

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  React hooks for Spoosh - `useRead`, `useWrite`, and `useInfiniteRead`.
4
4
 
5
- **[Documentation](https://spoosh.dev/docs/integrations/react)** · **Requirements:** TypeScript >= 5.0, React >= 18.0
5
+ **[Documentation](https://spoosh.dev/docs/react)** · **Requirements:** TypeScript >= 5.0, React >= 18.0
6
6
 
7
7
  ## Installation
8
8
 
package/dist/index.d.mts CHANGED
@@ -1,101 +1,5 @@
1
- import { ReadClient, TagMode, SpooshResponse, WriteClient, SpooshPlugin, PluginTypeConfig, SpooshOptions, MergePluginResults, StateManager, EventEmitter, PluginExecutor, PluginArray, ResolveTypes, MergePluginOptions, ResolverContext, ResolveResultTypes, MergePluginInstanceApi } from '@spoosh/core';
1
+ import { ReadClient, TagMode, SpooshResponse, SpooshPlugin, PluginTypeConfig, MergePluginResults, WriteClient, StateManager, EventEmitter, PluginExecutor, PluginArray, ResolveTypes, MergePluginOptions, ResolverContext, ResolveResultTypes, MergePluginInstanceApi, SpooshOptions } from '@spoosh/core';
2
2
 
3
- type PluginHooksConfig<TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = {
4
- baseUrl: string;
5
- defaultOptions?: SpooshOptions;
6
- plugins: TPlugins;
7
- };
8
- type TagModeInArray = "all" | "self";
9
- /**
10
- * Base options for `useRead` hook.
11
- */
12
- type BaseReadOptions = {
13
- /** Whether to fetch automatically on mount. Default: true */
14
- enabled?: boolean;
15
- /**
16
- * Unified tag option
17
- * - String: mode only ('all' | 'self' | 'none')
18
- * - Array: custom tags only OR [mode keyword mixed with custom tags]
19
- * - 'all' or 'self' can be used in arrays
20
- * - 'none' should only be used as string (use `tags: 'none'` not in array)
21
- */
22
- tags?: TagMode | (TagModeInArray | (string & {}))[];
23
- };
24
- /**
25
- * Result returned by `useRead` hook.
26
- *
27
- * @template TData - The response data type
28
- * @template TError - The error type
29
- * @template TMeta - Plugin-provided metadata fields
30
- * @template TTriggerOptions - Options that can be passed to trigger()
31
- */
32
- type BaseReadResult<TData, TError, TMeta = Record<string, unknown>, TTriggerOptions = {
33
- force?: boolean;
34
- }> = {
35
- /** True during the initial load (no data yet) */
36
- loading: boolean;
37
- /** True during any fetch operation */
38
- fetching: boolean;
39
- /** Response data from the API */
40
- data: TData | undefined;
41
- /** Error from the last failed request */
42
- error: TError | undefined;
43
- /** Plugin-provided metadata */
44
- meta: TMeta;
45
- /** Abort the current fetch operation */
46
- abort: () => void;
47
- /**
48
- * Manually trigger a fetch.
49
- *
50
- * @param options - Optional override options (query, body, params) to use for this specific request
51
- */
52
- trigger: (options?: TTriggerOptions) => Promise<SpooshResponse<TData, TError>>;
53
- };
54
- /**
55
- * Result returned by `useWrite` hook.
56
- *
57
- * @template TData - The response data type
58
- * @template TError - The error type
59
- * @template TOptions - The trigger options type
60
- * @template TMeta - Plugin-provided metadata fields
61
- */
62
- type BaseWriteResult<TData, TError, TOptions, TMeta = Record<string, unknown>> = {
63
- /** Execute the mutation with optional options */
64
- trigger: (options?: TOptions) => Promise<SpooshResponse<TData, TError>>;
65
- /** True while the mutation is in progress */
66
- loading: boolean;
67
- /** Response data from the API */
68
- data: TData | undefined;
69
- /** Error from the last failed request */
70
- error: TError | undefined;
71
- /** Plugin-provided metadata */
72
- meta: TMeta;
73
- /** Abort the current mutation */
74
- abort: () => void;
75
- };
76
- type OptionalQueryField<TQuery> = [TQuery] extends [never] ? object : undefined extends TQuery ? {
77
- query?: Exclude<TQuery, undefined>;
78
- } : {
79
- query: TQuery;
80
- };
81
- type OptionalBodyField<TBody> = [TBody] extends [never] ? object : undefined extends TBody ? {
82
- body?: Exclude<TBody, undefined>;
83
- } : {
84
- body: TBody;
85
- };
86
- type OptionalParamsField<TParamNames extends string> = [TParamNames] extends [
87
- never
88
- ] ? object : {
89
- params: Record<TParamNames, string | number>;
90
- };
91
- type InputFields<TQuery, TBody, TParamNames extends string> = OptionalQueryField<TQuery> & OptionalBodyField<TBody> & OptionalParamsField<TParamNames>;
92
- type WriteResponseInputFields<TQuery, TBody, TParamNames extends string> = [TQuery, TBody, TParamNames] extends [never, never, never] ? object : {
93
- input: InputFields<TQuery, TBody, TParamNames> | undefined;
94
- };
95
- type UseReadResult<TData, TError, TMeta, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = BaseReadResult<TData, TError, TMeta> & MergePluginResults<TPlugins>["read"];
96
- type UseWriteResult<TData, TError, TOptions, TMeta, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = BaseWriteResult<TData, TError, TOptions, TMeta> & MergePluginResults<TPlugins>["write"];
97
- type ReadApiClient<TSchema, TDefaultError> = ReadClient<TSchema, TDefaultError>;
98
- type WriteApiClient<TSchema, TDefaultError> = WriteClient<TSchema, TDefaultError>;
99
3
  type SuccessResponse<T> = Extract<T, {
100
4
  data: unknown;
101
5
  error?: undefined;
@@ -111,14 +15,19 @@ type ExtractMethodError<T> = T extends (...args: never[]) => infer R ? ErrorResp
111
15
  error: infer E;
112
16
  } ? E : unknown : unknown;
113
17
  type ExtractMethodOptions<T> = T extends (...args: infer A) => unknown ? A[0] : never;
18
+ type ExtractCoreMethodOptions<T> = T extends (...args: infer A) => unknown ? A[0] extends object ? Pick<A[0], Extract<keyof A[0], "query" | "params" | "body">> : object : object;
19
+ type AwaitedReturnType<T> = T extends (...args: never[]) => infer R ? Awaited<R> : never;
20
+ type SuccessReturnType<T> = SuccessResponse<AwaitedReturnType<T>>;
21
+ type ExtractSuccessInput<T> = SuccessResponse<AwaitedReturnType<T>> extends {
22
+ input?: infer I;
23
+ } ? I : object;
24
+ type ExtractResponseRequestOptions<T> = ExtractSuccessInput<T>;
114
25
  type ExtractMethodQuery<T> = ExtractMethodOptions<T> extends {
115
26
  query: infer Q;
116
27
  } ? Q : never;
117
28
  type ExtractMethodBody<T> = ExtractMethodOptions<T> extends {
118
29
  body: infer B;
119
30
  } ? B : never;
120
- type AwaitedReturnType<T> = T extends (...args: never[]) => infer R ? Awaited<R> : never;
121
- type SuccessReturnType<T> = SuccessResponse<AwaitedReturnType<T>>;
122
31
  type ExtractResponseQuery<T> = SuccessReturnType<T> extends {
123
32
  input: {
124
33
  query: infer Q;
@@ -134,6 +43,44 @@ type ExtractResponseParamNames<T> = SuccessReturnType<T> extends {
134
43
  params: Record<infer K, unknown>;
135
44
  };
136
45
  } ? K extends string ? K : never : never;
46
+
47
+ type TagModeInArray$1 = "all" | "self";
48
+ /**
49
+ * Base options for `useRead` hook.
50
+ */
51
+ type BaseReadOptions = {
52
+ /** Whether to fetch automatically on mount. Default: true */
53
+ enabled?: boolean;
54
+ /**
55
+ * Unified tag option
56
+ * - String: mode only ('all' | 'self' | 'none')
57
+ * - Array: custom tags only OR [mode keyword mixed with custom tags]
58
+ * - 'all' or 'self' can be used in arrays
59
+ * - 'none' should only be used as string (use `tags: 'none'` not in array)
60
+ */
61
+ tags?: TagMode | (TagModeInArray$1 | (string & {}))[];
62
+ };
63
+ type QueryField<TQuery> = [TQuery] extends [never] ? object : undefined extends TQuery ? {
64
+ query?: Exclude<TQuery, undefined>;
65
+ } : {
66
+ query: TQuery;
67
+ };
68
+ type BodyField<TBody> = [TBody] extends [never] ? object : undefined extends TBody ? {
69
+ body?: Exclude<TBody, undefined>;
70
+ } : {
71
+ body: TBody;
72
+ };
73
+ type ParamsField<TParamNames extends string> = [TParamNames] extends [never] ? object : {
74
+ params: Record<TParamNames, string | number>;
75
+ };
76
+ type ReadInputFields<TQuery, TBody, TParamNames extends string> = QueryField<TQuery> & BodyField<TBody> & ParamsField<TParamNames>;
77
+ type ResponseInputFields<TQuery, TBody, TParamNames extends string> = [
78
+ TQuery,
79
+ TBody,
80
+ TParamNames
81
+ ] extends [never, never, never] ? object : {
82
+ input: ReadInputFields<TQuery, TBody, TParamNames>;
83
+ };
137
84
  type TriggerAwaitedReturn<T> = T extends (...args: never[]) => infer R ? Awaited<R> : never;
138
85
  type ExtractInputFromResponse<T> = T extends {
139
86
  input: infer I;
@@ -161,27 +108,84 @@ type TriggerOptions<T> = ExtractInputFromResponse<TriggerAwaitedReturn<T>> exten
161
108
  } : {
162
109
  force?: boolean;
163
110
  };
164
- type QueryField<TQuery> = [TQuery] extends [never] ? object : undefined extends TQuery ? {
111
+ /**
112
+ * Result returned by `useRead` hook.
113
+ *
114
+ * @template TData - The response data type
115
+ * @template TError - The error type
116
+ * @template TMeta - Plugin-provided metadata fields
117
+ * @template TTriggerOptions - Options that can be passed to trigger()
118
+ */
119
+ type BaseReadResult<TData, TError, TMeta = Record<string, unknown>, TTriggerOptions = {
120
+ force?: boolean;
121
+ }> = {
122
+ /** True during the initial load (no data yet) */
123
+ loading: boolean;
124
+ /** True during any fetch operation */
125
+ fetching: boolean;
126
+ /** Response data from the API */
127
+ data: TData | undefined;
128
+ /** Error from the last failed request */
129
+ error: TError | undefined;
130
+ /** Plugin-provided metadata */
131
+ meta: TMeta;
132
+ /** Abort the current fetch operation */
133
+ abort: () => void;
134
+ /**
135
+ * Manually trigger a fetch.
136
+ *
137
+ * @param options - Optional override options (query, body, params) to use for this specific request
138
+ */
139
+ trigger: (options?: TTriggerOptions) => Promise<SpooshResponse<TData, TError>>;
140
+ };
141
+ type UseReadResult<TData, TError, TMeta, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = BaseReadResult<TData, TError, TMeta> & MergePluginResults<TPlugins>["read"];
142
+ type ReadApiClient<TSchema, TDefaultError> = ReadClient<TSchema, TDefaultError>;
143
+
144
+ type OptionalQueryField<TQuery> = [TQuery] extends [never] ? object : undefined extends TQuery ? {
165
145
  query?: Exclude<TQuery, undefined>;
166
146
  } : {
167
147
  query: TQuery;
168
148
  };
169
- type BodyField<TBody> = [TBody] extends [never] ? object : undefined extends TBody ? {
149
+ type OptionalBodyField<TBody> = [TBody] extends [never] ? object : undefined extends TBody ? {
170
150
  body?: Exclude<TBody, undefined>;
171
151
  } : {
172
152
  body: TBody;
173
153
  };
174
- type ParamsField<TParamNames extends string> = [TParamNames] extends [never] ? object : {
154
+ type OptionalParamsField<TParamNames extends string> = [TParamNames] extends [
155
+ never
156
+ ] ? object : {
175
157
  params: Record<TParamNames, string | number>;
176
158
  };
177
- type ReadInputFields<TQuery, TBody, TParamNames extends string> = QueryField<TQuery> & BodyField<TBody> & ParamsField<TParamNames>;
178
- type ResponseInputFields<TQuery, TBody, TParamNames extends string> = [
179
- TQuery,
180
- TBody,
181
- TParamNames
182
- ] extends [never, never, never] ? object : {
183
- input: ReadInputFields<TQuery, TBody, TParamNames>;
159
+ type InputFields<TQuery, TBody, TParamNames extends string> = OptionalQueryField<TQuery> & OptionalBodyField<TBody> & OptionalParamsField<TParamNames>;
160
+ type WriteResponseInputFields<TQuery, TBody, TParamNames extends string> = [TQuery, TBody, TParamNames] extends [never, never, never] ? object : {
161
+ input: InputFields<TQuery, TBody, TParamNames> | undefined;
162
+ };
163
+ /**
164
+ * Result returned by `useWrite` hook.
165
+ *
166
+ * @template TData - The response data type
167
+ * @template TError - The error type
168
+ * @template TOptions - The trigger options type
169
+ * @template TMeta - Plugin-provided metadata fields
170
+ */
171
+ type BaseWriteResult<TData, TError, TOptions, TMeta = Record<string, unknown>> = {
172
+ /** Execute the mutation with optional options */
173
+ trigger: (options?: TOptions) => Promise<SpooshResponse<TData, TError>>;
174
+ /** True while the mutation is in progress */
175
+ loading: boolean;
176
+ /** Response data from the API */
177
+ data: TData | undefined;
178
+ /** Error from the last failed request */
179
+ error: TError | undefined;
180
+ /** Plugin-provided metadata */
181
+ meta: TMeta;
182
+ /** Abort the current mutation */
183
+ abort: () => void;
184
184
  };
185
+ type UseWriteResult<TData, TError, TOptions, TMeta, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = BaseWriteResult<TData, TError, TOptions, TMeta> & MergePluginResults<TPlugins>["write"];
186
+ type WriteApiClient<TSchema, TDefaultError> = WriteClient<TSchema, TDefaultError>;
187
+
188
+ type TagModeInArray = "all" | "self";
185
189
  type AnyInfiniteRequestOptions = {
186
190
  query?: Record<string, unknown>;
187
191
  params?: Record<string, string | number>;
@@ -448,4 +452,10 @@ declare function createUseInfiniteRead<TSchema, TDefaultError, TPlugins extends
448
452
  readResult: infer R;
449
453
  } ? R : object : object : never : never) ? T_1 extends unknown ? (x: T_1) => void : never : never : never) extends (x: infer I) => void ? I : never>;
450
454
 
451
- export { type BaseInfiniteReadOptions, type BaseInfiniteReadResult, type BaseReadOptions, type BaseReadResult, type BaseWriteResult, type PluginHooksConfig, type SpooshReactHooks, type UseInfiniteReadResult, type UseReadResult, type UseWriteResult, createReactSpoosh, createUseInfiniteRead, createUseRead, createUseWrite };
455
+ type PluginHooksConfig<TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = {
456
+ baseUrl: string;
457
+ defaultOptions?: SpooshOptions;
458
+ plugins: TPlugins;
459
+ };
460
+
461
+ export { type AnyInfiniteRequestOptions, type BaseInfiniteReadOptions, type BaseInfiniteReadResult, type BaseReadOptions, type BaseReadResult, type BaseWriteResult, type ExtractCoreMethodOptions, type ExtractMethodBody, type ExtractMethodData, type ExtractMethodError, type ExtractMethodOptions, type ExtractMethodQuery, type ExtractResponseBody, type ExtractResponseParamNames, type ExtractResponseQuery, type ExtractResponseRequestOptions, type InfiniteNextContext, type InfinitePrevContext, type InfiniteReadApiClient, type PluginHooksConfig, type ReadApiClient, type ResponseInputFields, type SpooshReactHooks, type TriggerOptions, type UseInfiniteReadResult, type UseReadResult, type UseWriteResult, type WriteApiClient, type WriteResponseInputFields, createReactSpoosh, createUseInfiniteRead, createUseRead, createUseWrite };
package/dist/index.d.ts CHANGED
@@ -1,101 +1,5 @@
1
- import { ReadClient, TagMode, SpooshResponse, WriteClient, SpooshPlugin, PluginTypeConfig, SpooshOptions, MergePluginResults, StateManager, EventEmitter, PluginExecutor, PluginArray, ResolveTypes, MergePluginOptions, ResolverContext, ResolveResultTypes, MergePluginInstanceApi } from '@spoosh/core';
1
+ import { ReadClient, TagMode, SpooshResponse, SpooshPlugin, PluginTypeConfig, MergePluginResults, WriteClient, StateManager, EventEmitter, PluginExecutor, PluginArray, ResolveTypes, MergePluginOptions, ResolverContext, ResolveResultTypes, MergePluginInstanceApi, SpooshOptions } from '@spoosh/core';
2
2
 
3
- type PluginHooksConfig<TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = {
4
- baseUrl: string;
5
- defaultOptions?: SpooshOptions;
6
- plugins: TPlugins;
7
- };
8
- type TagModeInArray = "all" | "self";
9
- /**
10
- * Base options for `useRead` hook.
11
- */
12
- type BaseReadOptions = {
13
- /** Whether to fetch automatically on mount. Default: true */
14
- enabled?: boolean;
15
- /**
16
- * Unified tag option
17
- * - String: mode only ('all' | 'self' | 'none')
18
- * - Array: custom tags only OR [mode keyword mixed with custom tags]
19
- * - 'all' or 'self' can be used in arrays
20
- * - 'none' should only be used as string (use `tags: 'none'` not in array)
21
- */
22
- tags?: TagMode | (TagModeInArray | (string & {}))[];
23
- };
24
- /**
25
- * Result returned by `useRead` hook.
26
- *
27
- * @template TData - The response data type
28
- * @template TError - The error type
29
- * @template TMeta - Plugin-provided metadata fields
30
- * @template TTriggerOptions - Options that can be passed to trigger()
31
- */
32
- type BaseReadResult<TData, TError, TMeta = Record<string, unknown>, TTriggerOptions = {
33
- force?: boolean;
34
- }> = {
35
- /** True during the initial load (no data yet) */
36
- loading: boolean;
37
- /** True during any fetch operation */
38
- fetching: boolean;
39
- /** Response data from the API */
40
- data: TData | undefined;
41
- /** Error from the last failed request */
42
- error: TError | undefined;
43
- /** Plugin-provided metadata */
44
- meta: TMeta;
45
- /** Abort the current fetch operation */
46
- abort: () => void;
47
- /**
48
- * Manually trigger a fetch.
49
- *
50
- * @param options - Optional override options (query, body, params) to use for this specific request
51
- */
52
- trigger: (options?: TTriggerOptions) => Promise<SpooshResponse<TData, TError>>;
53
- };
54
- /**
55
- * Result returned by `useWrite` hook.
56
- *
57
- * @template TData - The response data type
58
- * @template TError - The error type
59
- * @template TOptions - The trigger options type
60
- * @template TMeta - Plugin-provided metadata fields
61
- */
62
- type BaseWriteResult<TData, TError, TOptions, TMeta = Record<string, unknown>> = {
63
- /** Execute the mutation with optional options */
64
- trigger: (options?: TOptions) => Promise<SpooshResponse<TData, TError>>;
65
- /** True while the mutation is in progress */
66
- loading: boolean;
67
- /** Response data from the API */
68
- data: TData | undefined;
69
- /** Error from the last failed request */
70
- error: TError | undefined;
71
- /** Plugin-provided metadata */
72
- meta: TMeta;
73
- /** Abort the current mutation */
74
- abort: () => void;
75
- };
76
- type OptionalQueryField<TQuery> = [TQuery] extends [never] ? object : undefined extends TQuery ? {
77
- query?: Exclude<TQuery, undefined>;
78
- } : {
79
- query: TQuery;
80
- };
81
- type OptionalBodyField<TBody> = [TBody] extends [never] ? object : undefined extends TBody ? {
82
- body?: Exclude<TBody, undefined>;
83
- } : {
84
- body: TBody;
85
- };
86
- type OptionalParamsField<TParamNames extends string> = [TParamNames] extends [
87
- never
88
- ] ? object : {
89
- params: Record<TParamNames, string | number>;
90
- };
91
- type InputFields<TQuery, TBody, TParamNames extends string> = OptionalQueryField<TQuery> & OptionalBodyField<TBody> & OptionalParamsField<TParamNames>;
92
- type WriteResponseInputFields<TQuery, TBody, TParamNames extends string> = [TQuery, TBody, TParamNames] extends [never, never, never] ? object : {
93
- input: InputFields<TQuery, TBody, TParamNames> | undefined;
94
- };
95
- type UseReadResult<TData, TError, TMeta, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = BaseReadResult<TData, TError, TMeta> & MergePluginResults<TPlugins>["read"];
96
- type UseWriteResult<TData, TError, TOptions, TMeta, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = BaseWriteResult<TData, TError, TOptions, TMeta> & MergePluginResults<TPlugins>["write"];
97
- type ReadApiClient<TSchema, TDefaultError> = ReadClient<TSchema, TDefaultError>;
98
- type WriteApiClient<TSchema, TDefaultError> = WriteClient<TSchema, TDefaultError>;
99
3
  type SuccessResponse<T> = Extract<T, {
100
4
  data: unknown;
101
5
  error?: undefined;
@@ -111,14 +15,19 @@ type ExtractMethodError<T> = T extends (...args: never[]) => infer R ? ErrorResp
111
15
  error: infer E;
112
16
  } ? E : unknown : unknown;
113
17
  type ExtractMethodOptions<T> = T extends (...args: infer A) => unknown ? A[0] : never;
18
+ type ExtractCoreMethodOptions<T> = T extends (...args: infer A) => unknown ? A[0] extends object ? Pick<A[0], Extract<keyof A[0], "query" | "params" | "body">> : object : object;
19
+ type AwaitedReturnType<T> = T extends (...args: never[]) => infer R ? Awaited<R> : never;
20
+ type SuccessReturnType<T> = SuccessResponse<AwaitedReturnType<T>>;
21
+ type ExtractSuccessInput<T> = SuccessResponse<AwaitedReturnType<T>> extends {
22
+ input?: infer I;
23
+ } ? I : object;
24
+ type ExtractResponseRequestOptions<T> = ExtractSuccessInput<T>;
114
25
  type ExtractMethodQuery<T> = ExtractMethodOptions<T> extends {
115
26
  query: infer Q;
116
27
  } ? Q : never;
117
28
  type ExtractMethodBody<T> = ExtractMethodOptions<T> extends {
118
29
  body: infer B;
119
30
  } ? B : never;
120
- type AwaitedReturnType<T> = T extends (...args: never[]) => infer R ? Awaited<R> : never;
121
- type SuccessReturnType<T> = SuccessResponse<AwaitedReturnType<T>>;
122
31
  type ExtractResponseQuery<T> = SuccessReturnType<T> extends {
123
32
  input: {
124
33
  query: infer Q;
@@ -134,6 +43,44 @@ type ExtractResponseParamNames<T> = SuccessReturnType<T> extends {
134
43
  params: Record<infer K, unknown>;
135
44
  };
136
45
  } ? K extends string ? K : never : never;
46
+
47
+ type TagModeInArray$1 = "all" | "self";
48
+ /**
49
+ * Base options for `useRead` hook.
50
+ */
51
+ type BaseReadOptions = {
52
+ /** Whether to fetch automatically on mount. Default: true */
53
+ enabled?: boolean;
54
+ /**
55
+ * Unified tag option
56
+ * - String: mode only ('all' | 'self' | 'none')
57
+ * - Array: custom tags only OR [mode keyword mixed with custom tags]
58
+ * - 'all' or 'self' can be used in arrays
59
+ * - 'none' should only be used as string (use `tags: 'none'` not in array)
60
+ */
61
+ tags?: TagMode | (TagModeInArray$1 | (string & {}))[];
62
+ };
63
+ type QueryField<TQuery> = [TQuery] extends [never] ? object : undefined extends TQuery ? {
64
+ query?: Exclude<TQuery, undefined>;
65
+ } : {
66
+ query: TQuery;
67
+ };
68
+ type BodyField<TBody> = [TBody] extends [never] ? object : undefined extends TBody ? {
69
+ body?: Exclude<TBody, undefined>;
70
+ } : {
71
+ body: TBody;
72
+ };
73
+ type ParamsField<TParamNames extends string> = [TParamNames] extends [never] ? object : {
74
+ params: Record<TParamNames, string | number>;
75
+ };
76
+ type ReadInputFields<TQuery, TBody, TParamNames extends string> = QueryField<TQuery> & BodyField<TBody> & ParamsField<TParamNames>;
77
+ type ResponseInputFields<TQuery, TBody, TParamNames extends string> = [
78
+ TQuery,
79
+ TBody,
80
+ TParamNames
81
+ ] extends [never, never, never] ? object : {
82
+ input: ReadInputFields<TQuery, TBody, TParamNames>;
83
+ };
137
84
  type TriggerAwaitedReturn<T> = T extends (...args: never[]) => infer R ? Awaited<R> : never;
138
85
  type ExtractInputFromResponse<T> = T extends {
139
86
  input: infer I;
@@ -161,27 +108,84 @@ type TriggerOptions<T> = ExtractInputFromResponse<TriggerAwaitedReturn<T>> exten
161
108
  } : {
162
109
  force?: boolean;
163
110
  };
164
- type QueryField<TQuery> = [TQuery] extends [never] ? object : undefined extends TQuery ? {
111
+ /**
112
+ * Result returned by `useRead` hook.
113
+ *
114
+ * @template TData - The response data type
115
+ * @template TError - The error type
116
+ * @template TMeta - Plugin-provided metadata fields
117
+ * @template TTriggerOptions - Options that can be passed to trigger()
118
+ */
119
+ type BaseReadResult<TData, TError, TMeta = Record<string, unknown>, TTriggerOptions = {
120
+ force?: boolean;
121
+ }> = {
122
+ /** True during the initial load (no data yet) */
123
+ loading: boolean;
124
+ /** True during any fetch operation */
125
+ fetching: boolean;
126
+ /** Response data from the API */
127
+ data: TData | undefined;
128
+ /** Error from the last failed request */
129
+ error: TError | undefined;
130
+ /** Plugin-provided metadata */
131
+ meta: TMeta;
132
+ /** Abort the current fetch operation */
133
+ abort: () => void;
134
+ /**
135
+ * Manually trigger a fetch.
136
+ *
137
+ * @param options - Optional override options (query, body, params) to use for this specific request
138
+ */
139
+ trigger: (options?: TTriggerOptions) => Promise<SpooshResponse<TData, TError>>;
140
+ };
141
+ type UseReadResult<TData, TError, TMeta, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = BaseReadResult<TData, TError, TMeta> & MergePluginResults<TPlugins>["read"];
142
+ type ReadApiClient<TSchema, TDefaultError> = ReadClient<TSchema, TDefaultError>;
143
+
144
+ type OptionalQueryField<TQuery> = [TQuery] extends [never] ? object : undefined extends TQuery ? {
165
145
  query?: Exclude<TQuery, undefined>;
166
146
  } : {
167
147
  query: TQuery;
168
148
  };
169
- type BodyField<TBody> = [TBody] extends [never] ? object : undefined extends TBody ? {
149
+ type OptionalBodyField<TBody> = [TBody] extends [never] ? object : undefined extends TBody ? {
170
150
  body?: Exclude<TBody, undefined>;
171
151
  } : {
172
152
  body: TBody;
173
153
  };
174
- type ParamsField<TParamNames extends string> = [TParamNames] extends [never] ? object : {
154
+ type OptionalParamsField<TParamNames extends string> = [TParamNames] extends [
155
+ never
156
+ ] ? object : {
175
157
  params: Record<TParamNames, string | number>;
176
158
  };
177
- type ReadInputFields<TQuery, TBody, TParamNames extends string> = QueryField<TQuery> & BodyField<TBody> & ParamsField<TParamNames>;
178
- type ResponseInputFields<TQuery, TBody, TParamNames extends string> = [
179
- TQuery,
180
- TBody,
181
- TParamNames
182
- ] extends [never, never, never] ? object : {
183
- input: ReadInputFields<TQuery, TBody, TParamNames>;
159
+ type InputFields<TQuery, TBody, TParamNames extends string> = OptionalQueryField<TQuery> & OptionalBodyField<TBody> & OptionalParamsField<TParamNames>;
160
+ type WriteResponseInputFields<TQuery, TBody, TParamNames extends string> = [TQuery, TBody, TParamNames] extends [never, never, never] ? object : {
161
+ input: InputFields<TQuery, TBody, TParamNames> | undefined;
162
+ };
163
+ /**
164
+ * Result returned by `useWrite` hook.
165
+ *
166
+ * @template TData - The response data type
167
+ * @template TError - The error type
168
+ * @template TOptions - The trigger options type
169
+ * @template TMeta - Plugin-provided metadata fields
170
+ */
171
+ type BaseWriteResult<TData, TError, TOptions, TMeta = Record<string, unknown>> = {
172
+ /** Execute the mutation with optional options */
173
+ trigger: (options?: TOptions) => Promise<SpooshResponse<TData, TError>>;
174
+ /** True while the mutation is in progress */
175
+ loading: boolean;
176
+ /** Response data from the API */
177
+ data: TData | undefined;
178
+ /** Error from the last failed request */
179
+ error: TError | undefined;
180
+ /** Plugin-provided metadata */
181
+ meta: TMeta;
182
+ /** Abort the current mutation */
183
+ abort: () => void;
184
184
  };
185
+ type UseWriteResult<TData, TError, TOptions, TMeta, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = BaseWriteResult<TData, TError, TOptions, TMeta> & MergePluginResults<TPlugins>["write"];
186
+ type WriteApiClient<TSchema, TDefaultError> = WriteClient<TSchema, TDefaultError>;
187
+
188
+ type TagModeInArray = "all" | "self";
185
189
  type AnyInfiniteRequestOptions = {
186
190
  query?: Record<string, unknown>;
187
191
  params?: Record<string, string | number>;
@@ -448,4 +452,10 @@ declare function createUseInfiniteRead<TSchema, TDefaultError, TPlugins extends
448
452
  readResult: infer R;
449
453
  } ? R : object : object : never : never) ? T_1 extends unknown ? (x: T_1) => void : never : never : never) extends (x: infer I) => void ? I : never>;
450
454
 
451
- export { type BaseInfiniteReadOptions, type BaseInfiniteReadResult, type BaseReadOptions, type BaseReadResult, type BaseWriteResult, type PluginHooksConfig, type SpooshReactHooks, type UseInfiniteReadResult, type UseReadResult, type UseWriteResult, createReactSpoosh, createUseInfiniteRead, createUseRead, createUseWrite };
455
+ type PluginHooksConfig<TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]> = {
456
+ baseUrl: string;
457
+ defaultOptions?: SpooshOptions;
458
+ plugins: TPlugins;
459
+ };
460
+
461
+ export { type AnyInfiniteRequestOptions, type BaseInfiniteReadOptions, type BaseInfiniteReadResult, type BaseReadOptions, type BaseReadResult, type BaseWriteResult, type ExtractCoreMethodOptions, type ExtractMethodBody, type ExtractMethodData, type ExtractMethodError, type ExtractMethodOptions, type ExtractMethodQuery, type ExtractResponseBody, type ExtractResponseParamNames, type ExtractResponseQuery, type ExtractResponseRequestOptions, type InfiniteNextContext, type InfinitePrevContext, type InfiniteReadApiClient, type PluginHooksConfig, type ReadApiClient, type ResponseInputFields, type SpooshReactHooks, type TriggerOptions, type UseInfiniteReadResult, type UseReadResult, type UseWriteResult, type WriteApiClient, type WriteResponseInputFields, createReactSpoosh, createUseInfiniteRead, createUseRead, createUseWrite };
package/dist/index.js CHANGED
@@ -128,7 +128,7 @@ function createUseRead(options) {
128
128
  return response;
129
129
  } catch (err) {
130
130
  setRequestState({ isPending: false, error: err });
131
- throw err;
131
+ return { error: err };
132
132
  }
133
133
  },
134
134
  [controller, capturedCall.options]
@@ -166,9 +166,13 @@ function createUseRead(options) {
166
166
  }
167
167
  }
168
168
  );
169
+ const unsubRefetchAll = eventEmitter.on("refetchAll", () => {
170
+ executeWithTracking(true);
171
+ });
169
172
  return () => {
170
173
  unsubRefetch();
171
174
  unsubInvalidate();
175
+ unsubRefetchAll();
172
176
  };
173
177
  }, [queryKey, enabled, tagsKey]);
174
178
  (0, import_react.useEffect)(() => {
@@ -181,7 +185,7 @@ function createUseRead(options) {
181
185
  }, []);
182
186
  const trigger = (0, import_react.useCallback)(
183
187
  async (triggerOptions) => {
184
- const { force = false, ...overrideOptions } = triggerOptions ?? {};
188
+ const { force = true, ...overrideOptions } = triggerOptions ?? {};
185
189
  const hasOverrides = Object.keys(overrideOptions).length > 0;
186
190
  if (!hasOverrides) {
187
191
  return executeWithTracking(force, void 0);
@@ -239,7 +243,7 @@ function createUseRead(options) {
239
243
  return response;
240
244
  } catch (err) {
241
245
  setRequestState({ isPending: false, error: err });
242
- throw err;
246
+ return { error: err };
243
247
  }
244
248
  },
245
249
  [
@@ -368,7 +372,7 @@ function createUseWrite(options) {
368
372
  return response;
369
373
  } catch (err) {
370
374
  setRequestState({ isPending: false, error: err });
371
- throw err;
375
+ return { error: err };
372
376
  }
373
377
  },
374
378
  [selectedEndpoint.path]
@@ -540,8 +544,13 @@ function createUseInfiniteRead(options) {
540
544
  }
541
545
  }
542
546
  );
547
+ const unsubRefetchAll = eventEmitter.on("refetchAll", () => {
548
+ setIsPending(true);
549
+ controller.refetch().finally(() => setIsPending(false));
550
+ });
543
551
  return () => {
544
552
  unsubInvalidate();
553
+ unsubRefetchAll();
545
554
  };
546
555
  }, [tagsKey]);
547
556
  (0, import_react3.useEffect)(() => {
package/dist/index.mjs CHANGED
@@ -112,7 +112,7 @@ function createUseRead(options) {
112
112
  return response;
113
113
  } catch (err) {
114
114
  setRequestState({ isPending: false, error: err });
115
- throw err;
115
+ return { error: err };
116
116
  }
117
117
  },
118
118
  [controller, capturedCall.options]
@@ -150,9 +150,13 @@ function createUseRead(options) {
150
150
  }
151
151
  }
152
152
  );
153
+ const unsubRefetchAll = eventEmitter.on("refetchAll", () => {
154
+ executeWithTracking(true);
155
+ });
153
156
  return () => {
154
157
  unsubRefetch();
155
158
  unsubInvalidate();
159
+ unsubRefetchAll();
156
160
  };
157
161
  }, [queryKey, enabled, tagsKey]);
158
162
  useEffect(() => {
@@ -165,7 +169,7 @@ function createUseRead(options) {
165
169
  }, []);
166
170
  const trigger = useCallback(
167
171
  async (triggerOptions) => {
168
- const { force = false, ...overrideOptions } = triggerOptions ?? {};
172
+ const { force = true, ...overrideOptions } = triggerOptions ?? {};
169
173
  const hasOverrides = Object.keys(overrideOptions).length > 0;
170
174
  if (!hasOverrides) {
171
175
  return executeWithTracking(force, void 0);
@@ -223,7 +227,7 @@ function createUseRead(options) {
223
227
  return response;
224
228
  } catch (err) {
225
229
  setRequestState({ isPending: false, error: err });
226
- throw err;
230
+ return { error: err };
227
231
  }
228
232
  },
229
233
  [
@@ -363,7 +367,7 @@ function createUseWrite(options) {
363
367
  return response;
364
368
  } catch (err) {
365
369
  setRequestState({ isPending: false, error: err });
366
- throw err;
370
+ return { error: err };
367
371
  }
368
372
  },
369
373
  [selectedEndpoint.path]
@@ -546,8 +550,13 @@ function createUseInfiniteRead(options) {
546
550
  }
547
551
  }
548
552
  );
553
+ const unsubRefetchAll = eventEmitter.on("refetchAll", () => {
554
+ setIsPending(true);
555
+ controller.refetch().finally(() => setIsPending(false));
556
+ });
549
557
  return () => {
550
558
  unsubInvalidate();
559
+ unsubRefetchAll();
551
560
  };
552
561
  }, [tagsKey]);
553
562
  useEffect2(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spoosh/react",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "license": "MIT",
5
5
  "description": "React hooks for Spoosh API client",
6
6
  "keywords": [
@@ -13,13 +13,13 @@
13
13
  ],
14
14
  "repository": {
15
15
  "type": "git",
16
- "url": "git+https://github.com/nxnom/spoosh.git",
16
+ "url": "git+https://github.com/spooshdev/spoosh.git",
17
17
  "directory": "packages/react"
18
18
  },
19
19
  "bugs": {
20
- "url": "https://github.com/nxnom/spoosh/issues"
20
+ "url": "https://github.com/spooshdev/spoosh/issues"
21
21
  },
22
- "homepage": "https://spoosh.dev/react/docs",
22
+ "homepage": "https://spoosh.dev/docs/react",
23
23
  "publishConfig": {
24
24
  "access": "public"
25
25
  },
@@ -38,7 +38,10 @@
38
38
  "react": "^18 || ^19"
39
39
  },
40
40
  "devDependencies": {
41
- "@spoosh/core": "0.9.0"
41
+ "@testing-library/react": "^16.0.0",
42
+ "jsdom": "^26.0.0",
43
+ "@spoosh/core": "0.10.0",
44
+ "@spoosh/test-utils": "0.1.5"
42
45
  },
43
46
  "scripts": {
44
47
  "dev": "tsup --watch",