@intrig/next 1.0.6 → 1.0.10

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.
Files changed (53) hide show
  1. package/eslint.config.cjs +19 -0
  2. package/jest.config.ts +10 -0
  3. package/package.json +7 -7
  4. package/project.json +20 -0
  5. package/src/extra/{index.d.ts → index.ts} +2 -2
  6. package/src/extra/useAsNetworkState.ts +53 -0
  7. package/src/extra/{useAsPromise.d.ts → useAsPromise.ts} +58 -7
  8. package/src/extra/useLocalReducer.ts +61 -0
  9. package/src/extra/{useResolvedCachedValue.d.ts → useResolvedCachedValue.ts} +39 -7
  10. package/src/extra/{useResolvedValue.d.ts → useResolvedValue.ts} +39 -7
  11. package/src/extra.ts +190 -0
  12. package/src/index.ts +3 -0
  13. package/src/intrig-context.ts +66 -0
  14. package/src/intrig-layout.tsx +18 -0
  15. package/src/intrig-middleware.ts +31 -0
  16. package/src/intrig-provider.tsx +454 -0
  17. package/src/logger.ts +13 -0
  18. package/src/media-type-utils.ts +184 -0
  19. package/src/{network-state.d.ts → network-state.tsx} +176 -91
  20. package/tsconfig.json +28 -0
  21. package/tsconfig.lib.json +10 -0
  22. package/tsconfig.spec.json +14 -0
  23. package/src/extra/index.js +0 -5
  24. package/src/extra/index.js.map +0 -1
  25. package/src/extra/useAsNetworkState.d.ts +0 -13
  26. package/src/extra/useAsNetworkState.js +0 -41
  27. package/src/extra/useAsNetworkState.js.map +0 -1
  28. package/src/extra/useAsPromise.js +0 -30
  29. package/src/extra/useAsPromise.js.map +0 -1
  30. package/src/extra/useResolvedCachedValue.js +0 -15
  31. package/src/extra/useResolvedCachedValue.js.map +0 -1
  32. package/src/extra/useResolvedValue.js +0 -17
  33. package/src/extra/useResolvedValue.js.map +0 -1
  34. package/src/extra.d.ts +0 -52
  35. package/src/extra.js +0 -92
  36. package/src/extra.js.map +0 -1
  37. package/src/index.d.ts +0 -5
  38. package/src/index.js +0 -6
  39. package/src/index.js.map +0 -1
  40. package/src/intrig-context.d.ts +0 -42
  41. package/src/intrig-context.js +0 -21
  42. package/src/intrig-context.js.map +0 -1
  43. package/src/intrig-middleware.d.ts +0 -1
  44. package/src/intrig-middleware.js +0 -15
  45. package/src/intrig-middleware.js.map +0 -1
  46. package/src/intrig-provider.d.ts +0 -101
  47. package/src/intrig-provider.js +0 -289
  48. package/src/intrig-provider.js.map +0 -1
  49. package/src/media-type-utils.d.ts +0 -3
  50. package/src/media-type-utils.js +0 -89
  51. package/src/media-type-utils.js.map +0 -1
  52. package/src/network-state.js +0 -185
  53. package/src/network-state.js.map +0 -1
package/src/extra.ts ADDED
@@ -0,0 +1,190 @@
1
+ "use client"
2
+ import {
3
+ BinaryFunctionHook,
4
+ BinaryHookOptions,
5
+ BinaryProduceHook,
6
+ ConstantHook,
7
+ error,
8
+ init, IntrigHook, IntrigHookOptions,
9
+ isError,
10
+ isSuccess,
11
+ isValidationError,
12
+ NetworkState,
13
+ pending,
14
+ success, UnaryFunctionHook, UnaryHookOptions, UnaryProduceHook,
15
+ UnitHook,
16
+ UnitHookOptions
17
+ } from '@intrig/next/network-state';
18
+ import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
19
+ import { useIntrigContext } from '@intrig/next/intrig-context';
20
+
21
+ /**
22
+ * Converts a given hook into a promise-based function.
23
+ *
24
+ * @param {IntrigHook<P, B, T>} hook - The hook function to be converted.
25
+ * @param options
26
+ *
27
+ * @return {[(...params: Parameters<ReturnType<IntrigHook<P, B, T>>[1]>) => Promise<T>, () => void]}
28
+ * Returns a tuple containing a function that invokes the hook as a promise and a function to clear the state.
29
+ */
30
+ export function useAsPromise<E>(hook: UnitHook<E>, options: UnitHookOptions): [() => Promise<never>, () => void];
31
+ export function useAsPromise<T, E>(hook: ConstantHook<T, E>, options: UnitHookOptions): [() => Promise<T>, () => void];
32
+ export function useAsPromise<P, E>(hook: UnaryProduceHook<P, E>, options?: UnaryHookOptions<P>): [(params: P) => Promise<never>, () => void];
33
+ export function useAsPromise<P, T, E>( hook: UnaryFunctionHook<P, T, E>, options?: UnaryHookOptions<P>): [(params: P) => Promise<T>, () => void];
34
+ export function useAsPromise<P, B, E>(hook: BinaryProduceHook<P, B, E>, options?: BinaryHookOptions<P, B>): [(body: B, params: P) => Promise<never>, () => void];
35
+ export function useAsPromise<P, B, T, E>(hook: BinaryFunctionHook<P, B, T, E>, options?: BinaryHookOptions<P, B>): [(body: B, params: P) => Promise<T>, () => void];
36
+
37
+ // **Implementation**
38
+ export function useAsPromise<P, B, T, E>(
39
+ hook: IntrigHook<P, B, T, E>,
40
+ options?: IntrigHookOptions<P, B>
41
+ ): [(...args: any[]) => Promise<T>, () => void] { // <- Compatible return type
42
+ const resolveRef = useRef<(value: T) => void>();
43
+ const rejectRef = useRef<(reason?: any) => void>();
44
+
45
+ const [state, dispatch, clear] = hook(options as any); // Casting to `any` to match all overloads
46
+
47
+ useEffect(() => {
48
+ if (isSuccess(state)) {
49
+ resolveRef.current?.(state.data);
50
+ clear();
51
+ } else if (isError(state)) {
52
+ rejectRef.current?.(state.error);
53
+ clear();
54
+ }
55
+ }, [state]);
56
+
57
+ const promiseFn = useCallback((...args: any[]) => {
58
+ return new Promise<T>((resolve, reject) => {
59
+ resolveRef.current = resolve;
60
+ rejectRef.current = reject;
61
+
62
+ const dispatchState = (dispatch as any)(...args);
63
+ if (isValidationError(dispatchState)) {
64
+ reject(dispatchState.error);
65
+ }
66
+ });
67
+ }, [dispatch]);
68
+
69
+ return [promiseFn, clear];
70
+ }
71
+
72
+ /**
73
+ * A custom hook that manages and returns the network state of a promise-based function,
74
+ * providing a way to execute the function and clear its state.
75
+ *
76
+ * @param fn The promise-based function whose network state is to be managed. It should be a function that returns a promise.
77
+ * @param key An optional identifier for the network state. Defaults to 'default'.
78
+ * @return A tuple containing the current network state, a function to execute the promise, and a function to clear the state.
79
+ */
80
+ export function useAsNetworkState<T, F extends ((...args: any) => Promise<T>)>(fn: F, key = 'default'): [NetworkState<T>, (...params: Parameters<F>) => void, () => void] {
81
+ const id = useId();
82
+
83
+ const context = useIntrigContext();
84
+
85
+ const networkState = useMemo(() => {
86
+ return context.state?.[`promiseState:${id}:${key}}`] ?? init()
87
+ }, [context.state?.[`promiseState:${id}:${key}}`]]);
88
+
89
+ const dispatch = useCallback(
90
+ (state: NetworkState<T>) => {
91
+ context.dispatch({ key, operation: id, source: 'promiseState', state });
92
+ },
93
+ [key, context.dispatch]
94
+ );
95
+
96
+ const execute = useCallback((...args: Parameters<F>) => {
97
+ dispatch(pending())
98
+ return fn(...args).then(
99
+ (data) => {
100
+ dispatch(success(data))
101
+ },
102
+ (e) => {
103
+ dispatch(error(e))
104
+ }
105
+ )
106
+ }, []);
107
+
108
+ const clear = useCallback(() => {
109
+ dispatch(init())
110
+ }, []);
111
+
112
+ return [
113
+ networkState,
114
+ execute,
115
+ clear
116
+ ]
117
+ }
118
+
119
+ /**
120
+ * A custom hook that resolves the value from the provided hook's state and updates it whenever the state changes.
121
+ *
122
+ * @param {IntrigHook<P, B, T>} hook - The hook that provides the state to observe and resolve data from.
123
+ * @param options
124
+ * @return {T | undefined} The resolved value from the hook's state or undefined if the state is not successful.
125
+ */
126
+ export function useResolvedValue<E>(hook: UnitHook<E>, options: UnitHookOptions): undefined;
127
+
128
+ export function useResolvedValue<T, E>(hook: ConstantHook<T, E>, options: UnitHookOptions): T | undefined;
129
+
130
+ export function useResolvedValue<P, E>(hook: UnaryProduceHook<P, E>, options: UnaryHookOptions<P>): undefined;
131
+
132
+ export function useResolvedValue<P, T, E>(hook: UnaryFunctionHook<P, T, E>, options: UnaryHookOptions<P>): T | undefined;
133
+
134
+ export function useResolvedValue<P, B, E>(hook: BinaryProduceHook<P, B, E>, options: BinaryHookOptions<P, B>): undefined;
135
+
136
+ export function useResolvedValue<P, B, T, E>(hook: BinaryFunctionHook<P, B, T, E>, options: BinaryHookOptions<P, B>): T | undefined;
137
+
138
+ // **Implementation**
139
+ export function useResolvedValue<P, B, T, E>(hook: IntrigHook<P, B, T, E>, options: IntrigHookOptions<P, B>): T | undefined {
140
+ const [value, setValue] = useState<T | undefined>();
141
+
142
+ const [state] = hook(options as any); // Ensure compatibility with different hook types
143
+
144
+ useEffect(() => {
145
+ if (isSuccess(state)) {
146
+ setValue(state.data);
147
+ } else {
148
+ setValue(undefined);
149
+ }
150
+ }, [state]); // Add `state` to the dependency array to ensure updates
151
+
152
+ return value;
153
+ }
154
+
155
+
156
+ /**
157
+ * A custom hook that resolves and caches the value from a successful state provided by the given hook.
158
+ * The state is updated only when it is in a successful state.
159
+ *
160
+ * @param {IntrigHook<P, B, T>} hook - The hook that provides the state to observe and cache data from.
161
+ * @param options
162
+ * @return {T | undefined} The cached value from the hook's state or undefined if the state is not successful.
163
+ */
164
+ export function useResolvedCachedValue<E>(hook: UnitHook<E>, options: UnitHookOptions): undefined;
165
+
166
+ export function useResolvedCachedValue<T, E>(hook: ConstantHook<T, E>, options: UnitHookOptions): T | undefined;
167
+
168
+ export function useResolvedCachedValue<P, E>(hook: UnaryProduceHook<P, E>, options: UnaryHookOptions<P>): undefined;
169
+
170
+ export function useResolvedCachedValue<P, T, E>(hook: UnaryFunctionHook<P, T, E>, options: UnaryHookOptions<P>): T | undefined;
171
+
172
+ export function useResolvedCachedValue<P, B, E>(hook: BinaryProduceHook<P, B, E>, options: BinaryHookOptions<P, B>): undefined;
173
+
174
+ export function useResolvedCachedValue<P, B, T, E>(hook: BinaryFunctionHook<P, B, T, E>, options: BinaryHookOptions<P, B>): T | undefined;
175
+
176
+ // **Implementation**
177
+ export function useResolvedCachedValue<P, B, T, E>(hook: IntrigHook<P, B, T, E>, options: IntrigHookOptions<P, B>): T | undefined {
178
+ const [cachedValue, setCachedValue] = useState<T | undefined>();
179
+
180
+ const [state] = hook(options as any); // Ensure compatibility with different hook types
181
+
182
+ useEffect(() => {
183
+ if (isSuccess(state)) {
184
+ setCachedValue(state.data);
185
+ }
186
+ // Do not clear cached value if state is unsuccessful
187
+ }, [state]);
188
+
189
+ return cachedValue;
190
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './intrig-provider';
2
+ export * from './network-state';
3
+ export * from './extra/index'
@@ -0,0 +1,66 @@
1
+ "use server"
2
+ import { NetworkAction, NetworkState } from '@intrig/next/network-state';
3
+ import { AxiosProgressEvent } from 'axios';
4
+ import { ZodSchema } from 'zod';
5
+ import { createContext, useContext } from 'react';
6
+ import { DefaultConfigs } from '@intrig/next/intrig-provider';
7
+
8
+ type GlobalState = Record<string, NetworkState>;
9
+
10
+ interface RequestType<T = any> {
11
+ method: 'get' | 'post' | 'put' | 'delete';
12
+ url: string;
13
+ headers?: Record<string, string>;
14
+ params?: Record<string, any>;
15
+ data?: any; // This allows transformations, while retaining flexibility.
16
+ originalData?: T; // Keeps track of the original data type.
17
+ onUploadProgress?: (event: AxiosProgressEvent) => void;
18
+ onDownloadProgress?: (event: AxiosProgressEvent) => void;
19
+ signal?: AbortSignal;
20
+ key: string;
21
+ }
22
+
23
+ /**
24
+ * Defines the ContextType interface for managing global state, dispatching actions,
25
+ * and holding a collection of Axios instances.
26
+ *
27
+ * @interface ContextType
28
+ * @property {GlobalState} state - The global state of the application.
29
+ * @property {React.Dispatch<NetworkAction<unknown>>} dispatch - The dispatch function to send network actions.
30
+ * @property {Record<string, AxiosInstance>} axios - A record of Axios instances for making HTTP requests.
31
+ */
32
+ export interface ContextType {
33
+ state: GlobalState;
34
+ filteredState: GlobalState;
35
+ dispatch: React.Dispatch<NetworkAction<unknown, unknown>>;
36
+ configs: DefaultConfigs;
37
+ execute: <T, E = unknown>(request: RequestType, dispatch: (state: NetworkState<T, E>) => void, schema: ZodSchema<T> | undefined, errorSchema: ZodSchema<E> | undefined) => Promise<void>;
38
+ }
39
+
40
+ /**
41
+ * Context object created using `createContext` function. Provides a way to share state, dispatch functions,
42
+ * and axios instance across components without having to pass props down manually at every level.
43
+ *
44
+ * @type {ContextType}
45
+ */
46
+ const Context = createContext<ContextType>({
47
+ state: {},
48
+ filteredState: {},
49
+ dispatch() {
50
+ //noop
51
+ },
52
+ configs: {},
53
+ async execute() {
54
+ //noop
55
+ }
56
+ });
57
+
58
+ export function useIntrigContext() {
59
+ return useContext(Context);
60
+ }
61
+
62
+ export {
63
+ Context,
64
+ GlobalState,
65
+ RequestType,
66
+ }
@@ -0,0 +1,18 @@
1
+ "use server"
2
+
3
+ import { headers } from 'next/headers';
4
+ import { DefaultConfigs, IntrigProvider } from './intrig-provider';
5
+
6
+ export default async function IntrigLayout({children, configs}: { children: React.ReactNode, configs?: DefaultConfigs}) {
7
+
8
+ const headersData = await headers();
9
+ const hydratedResponsesStr = headersData.get('INTRIG_HYDRATED');
10
+ const hydratedResponses = hydratedResponsesStr ? JSON.parse(hydratedResponsesStr) : {}
11
+ headersData.delete('INTRIG_HYDRATED');
12
+
13
+ return <>
14
+ <IntrigProvider configs={configs} initState={hydratedResponses}>
15
+ {children}
16
+ </IntrigProvider>
17
+ </>
18
+ }
@@ -0,0 +1,31 @@
1
+ "use server"
2
+
3
+ import axios from 'axios';
4
+ //@ts-expect-error: 'intrig-hook' is a dynamic module and may not be statically analyzable
5
+ const insightHook = await import('intrig-hook');
6
+ import {headers as requestHeaders} from 'next/headers'
7
+
8
+ export async function getAxiosInstance(key: string) {
9
+ const baseURL = process.env[`${key.toUpperCase()}_API_URL`];
10
+ if (!baseURL) {
11
+ throw new Error(
12
+ `Environment variable ${key.toUpperCase()}_API_URL is not defined.`
13
+ );
14
+ }
15
+
16
+ const axiosInstance = axios.create({ baseURL });
17
+
18
+ if (insightHook?.requestInterceptor) {
19
+ axiosInstance.interceptors.request.use(insightHook.requestInterceptor);
20
+ }
21
+
22
+ return axiosInstance;
23
+ }
24
+
25
+ export async function addResponseToHydrate(key: string, responseData: any) {
26
+ const _headers = await requestHeaders();
27
+ const intrigHydrated = _headers.get('INTRIG_HYDRATED');
28
+ const ob = intrigHydrated ? JSON.parse(intrigHydrated) : {};
29
+ ob[key] = responseData;
30
+ _headers.set('INTRIG_HYDRATED', JSON.stringify(ob));
31
+ }