@intrig/plugin-react 0.0.1

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 (55) hide show
  1. package/.swcrc +29 -0
  2. package/README.md +7 -0
  3. package/eslint.config.mjs +19 -0
  4. package/package.json +25 -0
  5. package/project.json +29 -0
  6. package/rollup.config.cjs +54 -0
  7. package/rollup.config.mjs +33 -0
  8. package/src/index.ts +2 -0
  9. package/src/lib/code-generator.ts +79 -0
  10. package/src/lib/get-endpoint-documentation.ts +35 -0
  11. package/src/lib/get-schema-documentation.ts +11 -0
  12. package/src/lib/internal-types.ts +15 -0
  13. package/src/lib/plugin-react.ts +22 -0
  14. package/src/lib/templates/context.template.ts +74 -0
  15. package/src/lib/templates/docs/__snapshots__/async-hook.spec.ts.snap +889 -0
  16. package/src/lib/templates/docs/__snapshots__/download-hook.spec.ts.snap +1445 -0
  17. package/src/lib/templates/docs/__snapshots__/react-hook.spec.ts.snap +1371 -0
  18. package/src/lib/templates/docs/__snapshots__/sse-hook.spec.ts.snap +2008 -0
  19. package/src/lib/templates/docs/async-hook.spec.ts +92 -0
  20. package/src/lib/templates/docs/async-hook.ts +226 -0
  21. package/src/lib/templates/docs/download-hook.spec.ts +182 -0
  22. package/src/lib/templates/docs/download-hook.ts +170 -0
  23. package/src/lib/templates/docs/react-hook.spec.ts +97 -0
  24. package/src/lib/templates/docs/react-hook.ts +323 -0
  25. package/src/lib/templates/docs/schema.ts +105 -0
  26. package/src/lib/templates/docs/sse-hook.spec.ts +207 -0
  27. package/src/lib/templates/docs/sse-hook.ts +221 -0
  28. package/src/lib/templates/extra.template.ts +198 -0
  29. package/src/lib/templates/index.template.ts +14 -0
  30. package/src/lib/templates/intrigMiddleware.template.ts +21 -0
  31. package/src/lib/templates/logger.template.ts +67 -0
  32. package/src/lib/templates/media-type-utils.template.ts +191 -0
  33. package/src/lib/templates/network-state.template.ts +702 -0
  34. package/src/lib/templates/packageJson.template.ts +63 -0
  35. package/src/lib/templates/provider/__tests__/provider-templates.spec.ts +209 -0
  36. package/src/lib/templates/provider/axios-config.template.ts +49 -0
  37. package/src/lib/templates/provider/hooks.template.ts +240 -0
  38. package/src/lib/templates/provider/interfaces.template.ts +72 -0
  39. package/src/lib/templates/provider/intrig-provider-stub.template.ts +73 -0
  40. package/src/lib/templates/provider/intrig-provider.template.ts +185 -0
  41. package/src/lib/templates/provider/main.template.ts +48 -0
  42. package/src/lib/templates/provider/reducer.template.ts +50 -0
  43. package/src/lib/templates/provider/status-trap.template.ts +80 -0
  44. package/src/lib/templates/provider.template.ts +698 -0
  45. package/src/lib/templates/source/controller/method/asyncFunctionHook.template.ts +196 -0
  46. package/src/lib/templates/source/controller/method/clientIndex.template.ts +38 -0
  47. package/src/lib/templates/source/controller/method/download.template.ts +256 -0
  48. package/src/lib/templates/source/controller/method/params.template.ts +31 -0
  49. package/src/lib/templates/source/controller/method/requestHook.template.ts +220 -0
  50. package/src/lib/templates/source/type/typeTemplate.ts +257 -0
  51. package/src/lib/templates/swcrc.template.ts +25 -0
  52. package/src/lib/templates/tsconfig.template.ts +37 -0
  53. package/src/lib/templates/type-utils.template.ts +28 -0
  54. package/tsconfig.json +13 -0
  55. package/tsconfig.lib.json +20 -0
@@ -0,0 +1,207 @@
1
+ import { reactSseHookDocs } from './sse-hook';
2
+
3
+ // We use a plain object cast to any to avoid direct dependency on the types from "@intrig/plugin-sdk" in test.
4
+
5
+ describe('reactSseHookDocs', () => {
6
+ it('snapshot — simple REST descriptor (no body, no path params)', async () => {
7
+ const descriptor = {
8
+ id: '1',
9
+ name: 'streamLogs',
10
+ type: 'rest' as const,
11
+ source: 'demo_api',
12
+ path: 'logs',
13
+ data: {
14
+ method: 'GET',
15
+ paths: ['/api/logs/stream'],
16
+ operationId: 'streamLogs',
17
+ // no requestBody
18
+ // no variables
19
+ },
20
+ };
21
+
22
+ const result = await reactSseHookDocs(descriptor as any);
23
+
24
+ expect(result.path).toBe('sse-hook.md');
25
+ expect(result.content).toMatchSnapshot();
26
+ });
27
+
28
+ it('snapshot — request body only (no path params)', async () => {
29
+ const descriptor = {
30
+ id: '2',
31
+ name: 'streamCustomEvents',
32
+ type: 'rest' as const,
33
+ source: 'demo_api',
34
+ path: 'events',
35
+ data: {
36
+ method: 'POST',
37
+ paths: ['/api/events/stream'],
38
+ operationId: 'streamCustomEvents',
39
+ requestBody: 'StreamCustomEventsRequest',
40
+ // no variables
41
+ },
42
+ };
43
+
44
+ const result = await reactSseHookDocs(descriptor as any);
45
+ expect(result.path).toBe('sse-hook.md');
46
+ expect(result.content).toMatchSnapshot();
47
+ });
48
+
49
+ it('snapshot — path params only (no request body)', async () => {
50
+ const descriptor = {
51
+ id: '3',
52
+ name: 'streamUserActivity',
53
+ type: 'rest' as const,
54
+ source: 'demo_api',
55
+ path: 'users/{userId}/activity',
56
+ data: {
57
+ method: 'GET',
58
+ paths: ['/api/users/{userId}/activity/stream'],
59
+ operationId: 'streamUserActivity',
60
+ variables: [
61
+ { name: 'userId', in: 'path', ref: '#/components/schemas/UserId' },
62
+ ],
63
+ },
64
+ };
65
+
66
+ const result = await reactSseHookDocs(descriptor as any);
67
+ expect(result.path).toBe('sse-hook.md');
68
+ expect(result.content).toMatchSnapshot();
69
+ });
70
+
71
+ it('snapshot — request body and path params', async () => {
72
+ const descriptor = {
73
+ id: '4',
74
+ name: 'streamTaskProgress',
75
+ type: 'rest' as const,
76
+ source: 'demo_api',
77
+ path: 'tasks/{taskId}/progress',
78
+ data: {
79
+ method: 'POST',
80
+ paths: ['/api/tasks/{taskId}/progress/stream'],
81
+ operationId: 'streamTaskProgress',
82
+ requestBody: 'StreamTaskProgressRequest',
83
+ variables: [
84
+ { name: 'taskId', in: 'PATH', ref: '#/components/schemas/TaskId' },
85
+ ],
86
+ },
87
+ };
88
+
89
+ const result = await reactSseHookDocs(descriptor as any);
90
+ expect(result.path).toBe('sse-hook.md');
91
+ expect(result.content).toMatchSnapshot();
92
+ });
93
+
94
+ it('handles multiple path params', async () => {
95
+ const descriptor = {
96
+ id: '5',
97
+ name: 'streamProjectNotifications',
98
+ type: 'rest' as const,
99
+ source: 'demo_api',
100
+ path: 'projects/{projectId}/notifications/{notificationId}',
101
+ data: {
102
+ method: 'GET',
103
+ paths: ['/api/projects/{projectId}/notifications/{notificationId}/stream'],
104
+ operationId: 'streamProjectNotifications',
105
+ variables: [
106
+ { name: 'projectId', in: 'path', ref: '#/components/schemas/ProjectId' },
107
+ { name: 'notificationId', in: 'PATH', ref: '#/components/schemas/NotificationId' },
108
+ ],
109
+ },
110
+ };
111
+
112
+ const result = await reactSseHookDocs(descriptor as any);
113
+ expect(result.path).toBe('sse-hook.md');
114
+ expect(result.content).toMatchSnapshot();
115
+ });
116
+
117
+ it('handles query params mixed with path params', async () => {
118
+ const descriptor = {
119
+ id: '6',
120
+ name: 'streamSystemMetrics',
121
+ type: 'rest' as const,
122
+ source: 'demo_api',
123
+ path: 'systems/{systemId}/metrics',
124
+ data: {
125
+ method: 'GET',
126
+ paths: ['/api/systems/{systemId}/metrics/stream'],
127
+ operationId: 'streamSystemMetrics',
128
+ variables: [
129
+ { name: 'systemId', in: 'path', ref: '#/components/schemas/SystemId' },
130
+ { name: 'interval', in: 'query', ref: '#/components/schemas/Interval' },
131
+ { name: 'includeDetails', in: 'QUERY', ref: '#/components/schemas/Boolean' },
132
+ ],
133
+ },
134
+ };
135
+
136
+ const result = await reactSseHookDocs(descriptor as any);
137
+ expect(result.path).toBe('sse-hook.md');
138
+ expect(result.content).toMatchSnapshot();
139
+ });
140
+
141
+ it('handles empty variables array', async () => {
142
+ const descriptor = {
143
+ id: '7',
144
+ name: 'streamGlobalEvents',
145
+ type: 'rest' as const,
146
+ source: 'demo_api',
147
+ path: 'events',
148
+ data: {
149
+ method: 'GET',
150
+ paths: ['/api/events/global/stream'],
151
+ operationId: 'streamGlobalEvents',
152
+ variables: [], // empty array instead of undefined
153
+ },
154
+ };
155
+
156
+ const result = await reactSseHookDocs(descriptor as any);
157
+ expect(result.path).toBe('sse-hook.md');
158
+ expect(result.content).toMatchSnapshot();
159
+ });
160
+
161
+ it('handles case-insensitive path parameter detection', async () => {
162
+ const descriptor = {
163
+ id: '8',
164
+ name: 'streamOrderUpdates',
165
+ type: 'rest' as const,
166
+ source: 'demo_api',
167
+ path: 'orders/{orderId}',
168
+ data: {
169
+ method: 'GET',
170
+ paths: ['/api/orders/{orderId}/updates/stream'],
171
+ operationId: 'streamOrderUpdates',
172
+ variables: [
173
+ { name: 'orderId', in: 'Path', ref: '#/components/schemas/OrderId' }, // Mixed case
174
+ ],
175
+ },
176
+ };
177
+
178
+ const result = await reactSseHookDocs(descriptor as any);
179
+ expect(result.path).toBe('sse-hook.md');
180
+ expect(result.content).toMatchSnapshot();
181
+ });
182
+
183
+ it('handles complex SSE endpoint with body and multiple params', async () => {
184
+ const descriptor = {
185
+ id: '9',
186
+ name: 'streamAnalytics',
187
+ type: 'rest' as const,
188
+ source: 'demo_api',
189
+ path: 'analytics/{dashboardId}/stream',
190
+ data: {
191
+ method: 'POST',
192
+ paths: ['/api/analytics/{dashboardId}/stream'],
193
+ operationId: 'streamAnalytics',
194
+ requestBody: 'StreamAnalyticsRequest',
195
+ variables: [
196
+ { name: 'dashboardId', in: 'path', ref: '#/components/schemas/DashboardId' },
197
+ { name: 'real_time', in: 'query', ref: '#/components/schemas/Boolean' },
198
+ { name: 'format', in: 'query', ref: '#/components/schemas/Format' },
199
+ ],
200
+ },
201
+ };
202
+
203
+ const result = await reactSseHookDocs(descriptor as any);
204
+ expect(result.path).toBe('sse-hook.md');
205
+ expect(result.content).toMatchSnapshot();
206
+ });
207
+ });
@@ -0,0 +1,221 @@
1
+ import { camelCase, mdLiteral, pascalCase, ResourceDescriptor, RestData } from "@intrig/plugin-sdk";
2
+
3
+ export function reactSseHookDocs(descriptor: ResourceDescriptor<RestData>) {
4
+ const md = mdLiteral("sse-hook.md");
5
+
6
+ // ===== Derived names =====
7
+ const hasPathParams = (descriptor.data.variables ?? []).some(
8
+ (v: any) => v.in?.toUpperCase() === "PATH",
9
+ );
10
+
11
+ const actionName = camelCase(descriptor.name); // e.g. streamBuildLogs
12
+ const respVar = `${actionName}Resp`; // e.g. streamBuildLogsResp
13
+ const clearName = `clear${pascalCase(descriptor.name)}`; // e.g. clearStreamBuildLogs
14
+
15
+ const requestBodyVar = descriptor.data.requestBody
16
+ ? camelCase(descriptor.data.requestBody)
17
+ : undefined;
18
+ const requestBodyType = descriptor.data.requestBody
19
+ ? pascalCase(descriptor.data.requestBody)
20
+ : undefined;
21
+
22
+ const paramsVar = hasPathParams ? `${actionName}Params` : undefined; // e.g. streamBuildLogsParams
23
+ const paramsType = hasPathParams ? `${pascalCase(descriptor.name)}Params` : undefined; // e.g. StreamBuildLogsParams
24
+
25
+ const responseTypeName = `${pascalCase(descriptor.name)}ResponseBody`; // if generated by your build
26
+ const callArgs = [requestBodyVar, paramsVar ?? "{}"].filter(Boolean).join(", ");
27
+
28
+ return md`
29
+ # Intrig SSE Hooks — Quick Guide
30
+
31
+ ## When should I use the SSE hook?
32
+ - **Your endpoint streams events** (Server-Sent Events) and you want **incremental updates** in the UI → use this **SSE hook**.
33
+ - **You only need a final result** → use the regular **stateful hook**.
34
+ - **One-off validate/submit/update** with no shared state → use the **async hook**.
35
+
36
+ > Intrig SSE hooks are **stateful hooks** under the hood. **Events arrive while the hook is in \`Pending\`**. When the stream completes, the hook transitions to **\`Success\`** (or **\`Error\`**).
37
+
38
+ ---
39
+
40
+ ## Copy-paste starter (fast lane)
41
+
42
+ ### 1) Hook import
43
+ \`\`\`ts
44
+ import { use${pascalCase(descriptor.name)} } from '@intrig/react/${descriptor.path}/client';
45
+ \`\`\`
46
+
47
+ ### 2) Utility guards
48
+ \`\`\`ts
49
+ import { isPending, isSuccess, isError } from '@intrig/react';
50
+ \`\`\`
51
+
52
+ ### 3) Hook instance (auto-clear on unmount)
53
+ \`\`\`ts
54
+ const [${respVar}, ${actionName}] = use${pascalCase(descriptor.name)}({ clearOnUnmount: true });
55
+ \`\`\`
56
+
57
+ ---
58
+
59
+ ## TL;DR (copy–paste)
60
+
61
+ \`\`\`tsx
62
+ import { use${pascalCase(descriptor.name)} } from '@intrig/react/${descriptor.path}/client';
63
+ import { isPending, isSuccess, isError } from '@intrig/react';
64
+ import { useEffect, useState } from 'react';
65
+
66
+ export default function Example() {
67
+ const [${respVar}, ${actionName}] = use${pascalCase(descriptor.name)}({ clearOnUnmount: true });
68
+ const [messages, setMessages] = useState<any[]>([]);
69
+
70
+ useEffect(() => {
71
+ ${actionName}(${callArgs}); // start stream
72
+ }, [${actionName}]);
73
+
74
+ useEffect(() => {
75
+ // SSE delivers messages while state is Pending
76
+ if (isPending(${respVar})) {
77
+ setMessages((prev) => [...prev, ${respVar}.data]);
78
+ }
79
+ }, [${respVar}]);
80
+
81
+ if (isError(${respVar})) return <>An error occurred</>;
82
+ if (isPending(${respVar})) return <pre>{JSON.stringify(messages, null, 2)}</pre>;
83
+ if (isSuccess(${respVar})) return <>Completed</>;
84
+
85
+ return null;
86
+ }
87
+ \`\`\`
88
+
89
+ ${(requestBodyType || paramsType) ? `### Optional types (if generated by your build)
90
+ \`\`\`ts
91
+ ${requestBodyType ? `import type { ${requestBodyType} } from '@intrig/react/${descriptor.source}/components/schemas/${requestBodyType}';\n` : ''}${paramsType ? `import type { ${paramsType} } from '@intrig/react/${descriptor.path}/${pascalCase(descriptor.name)}.params';\n` : ''}import type { ${responseTypeName} } from '@intrig/react/${descriptor.path}/${pascalCase(descriptor.name)}.response';
92
+ \`\`\`
93
+ ` : ''}
94
+
95
+ ---
96
+
97
+ ## Hook API
98
+
99
+ \`\`\`ts
100
+ // Signature (shape shown; concrete generics vary per generated hook)
101
+ declare function use${pascalCase(descriptor.name)}(options?: {
102
+ fetchOnMount?: boolean;
103
+ clearOnUnmount?: boolean; // recommended for streams
104
+ key?: string; // isolate multiple subscriptions
105
+ params?: ${paramsType ?? 'unknown'};
106
+ body?: ${requestBodyType ?? 'unknown'};
107
+ }): [
108
+ // While streaming: isPending(state) === true and state.data is the latest event
109
+ NetworkState<${responseTypeName} /* or event payload type */, any>,
110
+ // Start streaming:
111
+ (req: { params?: ${paramsType ?? 'unknown'}; body?: ${requestBodyType ?? 'unknown'} }) => void,
112
+ // Clear/close stream:
113
+ () => void
114
+ ];
115
+ \`\`\`
116
+
117
+ > **Important:** For SSE, **each incoming event** is surfaced as \`${respVar}.data\` **only while** \`isPending(${respVar})\` is true. On stream completion the hook flips to \`isSuccess\`.
118
+
119
+ ---
120
+
121
+ ## Usage patterns
122
+
123
+ ### 1) Lifecycle-bound stream (start on mount, auto-clear)
124
+ \`\`\`tsx
125
+ const [${respVar}, ${actionName}] = use${pascalCase(descriptor.name)}({ clearOnUnmount: true });
126
+
127
+ useEffect(() => {
128
+ ${actionName}(${callArgs});
129
+ }, [${actionName}]);
130
+ \`\`\`
131
+ <details><summary>Description</summary>
132
+ Starts the stream when the component mounts and closes it when the component unmounts.
133
+ </details>
134
+
135
+ ### 2) Collect messages into an array (simple collector)
136
+ \`\`\`tsx
137
+ const [messages, setMessages] = useState<any[]>([]);
138
+
139
+ useEffect(() => {
140
+ if (isPending(${respVar})) setMessages((m) => [...m, ${respVar}.data]);
141
+ }, [${respVar}]);
142
+ \`\`\`
143
+ <details><summary>Description</summary>
144
+ Appends each event to an in-memory array. Good for logs and chat-like feeds; consider capping length to avoid memory growth.
145
+ </details>
146
+
147
+ ### 3) Keep only the latest event (cheap UI)
148
+ \`\`\`tsx
149
+ const latest = isPending(${respVar}) ? ${respVar}.data : undefined;
150
+ \`\`\`
151
+ <details><summary>Description</summary>
152
+ When you only need the most recent message (progress percentage, status line).
153
+ </details>
154
+
155
+ ### 4) Controlled start/stop (user-triggered)
156
+ \`\`\`tsx
157
+ const [${respVar}, ${actionName}, ${clearName}] = use${pascalCase(descriptor.name)}();
158
+
159
+ const start = () => ${actionName}(${callArgs});
160
+ const stop = () => ${clearName}();
161
+ \`\`\`
162
+ <details><summary>Description</summary>
163
+ Expose play/pause UI for long streams or admin tools.
164
+ </details>
165
+
166
+ ---
167
+
168
+ ## Full example (with flushSync option)
169
+
170
+ \`\`\`tsx
171
+ import { use${pascalCase(descriptor.name)} } from '@intrig/react/${descriptor.path}/client';
172
+ import { isPending, isSuccess, isError } from '@intrig/react';
173
+ import { useEffect, useState } from 'react';
174
+ import { flushSync } from 'react-dom';
175
+
176
+ function MyComponent() {
177
+ const [${respVar}, ${actionName}] = use${pascalCase(descriptor.name)}({ clearOnUnmount: true });
178
+ const [events, setEvents] = useState<any[]>([]);
179
+
180
+ useEffect(() => {
181
+ ${actionName}(${callArgs});
182
+ }, [${actionName}]);
183
+
184
+ useEffect(() => {
185
+ if (isPending(${respVar})) {
186
+ // Use flushSync only if you must render every single event (high-frequency streams).
187
+ flushSync(() => setEvents((xs) => [...xs, ${respVar}.data]));
188
+ }
189
+ }, [${respVar}]);
190
+
191
+ if (isError(${respVar})) return <>Stream error</>;
192
+ return (
193
+ <>
194
+ {isPending(${respVar}) && <pre>{JSON.stringify(events, null, 2)}</pre>}
195
+ {isSuccess(${respVar}) && <>Completed ({events.length} events)</>}
196
+ </>
197
+ );
198
+ }
199
+ \`\`\`
200
+
201
+ ---
202
+
203
+ ## Tips, anti-patterns & gotchas
204
+
205
+ - **Prefer \`clearOnUnmount: true\`** so the EventSource/web request is closed when the component disappears.
206
+ - **Don’t store unbounded arrays** for infinite streams—cap the length or batch to IndexedDB.
207
+ - **Avoid unnecessary \`flushSync\`**; it’s expensive. Use it only when you truly must render every event.
208
+ - **Multiple streams:** supply a unique \`key\` to isolate independent subscriptions.
209
+ - **Server requirements:** SSE endpoints should send \`Content-Type: text/event-stream\`, disable buffering, and flush regularly; add relevant CORS headers if needed.
210
+ - **Completion:** UI can switch from progress view (\`isPending\`) to final view (\`isSuccess\`) automatically.
211
+
212
+ ---
213
+
214
+ ## Troubleshooting
215
+
216
+ - **No intermediate messages:** ensure the server is truly streaming SSE (correct content type + flush) and that proxies/CDNs aren’t buffering responses.
217
+ - **UI not updating for each event:** remove expensive work from the event effect, consider throttling; only use \`flushSync\` if absolutely necessary.
218
+ - **Stream never completes:** check server end conditions and that you call \`${clearName}\` when appropriate.
219
+
220
+ `;
221
+ }
@@ -0,0 +1,198 @@
1
+ import {typescript} from "@intrig/plugin-sdk";
2
+ import * as path from 'path'
3
+
4
+ export function reactExtraTemplate() {
5
+ const ts = typescript(path.resolve("src", "extra.ts"))
6
+
7
+ return ts`import {
8
+ BinaryFunctionHook,
9
+ BinaryHookOptions,
10
+ BinaryProduceHook,
11
+ ConstantHook,
12
+ error,
13
+ init,
14
+ IntrigHook,
15
+ IntrigHookOptions,
16
+ isSuccess,
17
+ NetworkState,
18
+ pending,
19
+ success,
20
+ UnaryFunctionHook,
21
+ UnaryHookOptions,
22
+ UnaryProduceHook,
23
+ UnitHook,
24
+ UnitHookOptions,
25
+ } from '@intrig/react/network-state';
26
+ import {
27
+ useCallback,
28
+ useEffect,
29
+ useId,
30
+ useMemo,
31
+ useState,
32
+ } from 'react';
33
+ import { useIntrigContext } from '@intrig/react/intrig-context';
34
+
35
+ /**
36
+ * A custom hook that manages and returns the network state of a promise-based function,
37
+ * providing a way to execute the function and clear its state.
38
+ *
39
+ * @param fn The promise-based function whose network state is to be managed. It should be a function that returns a promise.
40
+ * @param key An optional identifier for the network state. Defaults to 'default'.
41
+ * @return A tuple containing the current network state, a function to execute the promise, and a function to clear the state.
42
+ */
43
+ export function useAsNetworkState<T, F extends (...args: any) => Promise<T>>(
44
+ fn: F,
45
+ options: any = {},
46
+ ): [NetworkState<T>, (...params: Parameters<F>) => void, () => void] {
47
+ const id = useId();
48
+
49
+ const context = useIntrigContext();
50
+
51
+ const key = options.key ?? 'default';
52
+
53
+ const networkState = useMemo(() => {
54
+ return context.state?.[${"`promiseState:${id}:${key}}`"}] ?? init();
55
+ }, [context.state?.[${"`promiseState:${id}:${key}}`"}]]);
56
+
57
+ const dispatch = useCallback(
58
+ (state: NetworkState<T>) => {
59
+ context.dispatch({ key, operation: id, source: 'promiseState', state });
60
+ },
61
+ [key, context.dispatch],
62
+ );
63
+
64
+ const execute = useCallback((...args: any[]) => {
65
+ dispatch(pending());
66
+ return fn(...args).then(
67
+ (data) => {
68
+ dispatch(success(data));
69
+ },
70
+ (e) => {
71
+ dispatch(error(e));
72
+ },
73
+ );
74
+ }, []);
75
+
76
+ const clear = useCallback(() => {
77
+ dispatch(init());
78
+ }, []);
79
+
80
+ return [networkState, execute, clear];
81
+ }
82
+
83
+ /**
84
+ * A custom hook that resolves the value from the provided hook's state and updates it whenever the state changes.
85
+ *
86
+ * @param {IntrigHook<P, B, T>} hook - The hook that provides the state to observe and resolve data from.
87
+ * @param options
88
+ * @return {T | undefined} The resolved value from the hook's state or undefined if the state is not successful.
89
+ */
90
+ export function useResolvedValue(
91
+ hook: UnitHook,
92
+ options: UnitHookOptions,
93
+ ): undefined;
94
+
95
+ export function useResolvedValue<T>(
96
+ hook: ConstantHook<T>,
97
+ options: UnitHookOptions,
98
+ ): T | undefined;
99
+
100
+ export function useResolvedValue<P>(
101
+ hook: UnaryProduceHook<P>,
102
+ options: UnaryHookOptions<P>,
103
+ ): undefined;
104
+
105
+ export function useResolvedValue<P, T>(
106
+ hook: UnaryFunctionHook<P, T>,
107
+ options: UnaryHookOptions<P>,
108
+ ): T | undefined;
109
+
110
+ export function useResolvedValue<P, B>(
111
+ hook: BinaryProduceHook<P, B>,
112
+ options: BinaryHookOptions<P, B>,
113
+ ): undefined;
114
+
115
+ export function useResolvedValue<P, B, T>(
116
+ hook: BinaryFunctionHook<P, B, T>,
117
+ options: BinaryHookOptions<P, B>,
118
+ ): T | undefined;
119
+
120
+ // **Implementation**
121
+ export function useResolvedValue<P, B, T>(
122
+ hook: IntrigHook<P, B, T>,
123
+ options: IntrigHookOptions<P, B>,
124
+ ): T | undefined {
125
+ const [value, setValue] = useState<T | undefined>();
126
+
127
+ const [state] = hook(options as any); // Ensure compatibility with different hook types
128
+
129
+ useEffect(() => {
130
+ if (isSuccess(state)) {
131
+ setValue(state.data);
132
+ } else {
133
+ setValue(undefined);
134
+ }
135
+ }, [state]);
136
+
137
+ return value;
138
+ }
139
+
140
+ /**
141
+ * A custom hook that resolves and caches the value from a successful state provided by the given hook.
142
+ * The state is updated only when it is in a successful state.
143
+ *
144
+ * @param {IntrigHook<P, B, T>} hook - The hook that provides the state to observe and cache data from.
145
+ * @param options
146
+ * @return {T | undefined} The cached value from the hook's state or undefined if the state is not successful.
147
+ */
148
+ export function useResolvedCachedValue(
149
+ hook: UnitHook,
150
+ options: UnitHookOptions,
151
+ ): undefined;
152
+
153
+ export function useResolvedCachedValue<T>(
154
+ hook: ConstantHook<T>,
155
+ options: UnitHookOptions,
156
+ ): T | undefined;
157
+
158
+ export function useResolvedCachedValue<P>(
159
+ hook: UnaryProduceHook<P>,
160
+ options: UnaryHookOptions<P>,
161
+ ): undefined;
162
+
163
+ export function useResolvedCachedValue<P, T>(
164
+ hook: UnaryFunctionHook<P, T>,
165
+ options: UnaryHookOptions<P>,
166
+ ): T | undefined;
167
+
168
+ export function useResolvedCachedValue<P, B>(
169
+ hook: BinaryProduceHook<P, B>,
170
+ options: BinaryHookOptions<P, B>,
171
+ ): undefined;
172
+
173
+ export function useResolvedCachedValue<P, B, T>(
174
+ hook: BinaryFunctionHook<P, B, T>,
175
+ options: BinaryHookOptions<P, B>,
176
+ ): T | undefined;
177
+
178
+ // **Implementation**
179
+ export function useResolvedCachedValue<P, B, T>(
180
+ hook: IntrigHook<P, B, T>,
181
+ options: IntrigHookOptions<P, B>,
182
+ ): T | undefined {
183
+ const [cachedValue, setCachedValue] = useState<T | undefined>();
184
+
185
+ const [state] = hook(options as any); // Ensure compatibility with different hook types
186
+
187
+ useEffect(() => {
188
+ if (isSuccess(state)) {
189
+ setCachedValue(state.data);
190
+ }
191
+ // Do not clear cached value if state is unsuccessful
192
+ }, [state]);
193
+
194
+ return cachedValue;
195
+ }
196
+
197
+ `
198
+ }
@@ -0,0 +1,14 @@
1
+ import {typescript} from "@intrig/plugin-sdk";
2
+ import * as path from 'path'
3
+
4
+ export function indexTemplate(){
5
+
6
+ const ts = typescript(path.resolve("src", "index.ts"))
7
+
8
+ return ts`
9
+ export * from './intrig-provider-main';
10
+ export * from './network-state';
11
+ export * from './extra';
12
+ export * from './media-type-utils';
13
+ `
14
+ }
@@ -0,0 +1,21 @@
1
+ import { typescript } from "@intrig/plugin-sdk";
2
+ import * as path from 'path'
3
+
4
+ export function intrigMiddlewareTemplate() {
5
+ const ts = typescript(path.resolve('src', 'intrig-middleware.ts'))
6
+
7
+ return ts`
8
+ import axios from 'axios';
9
+ import { requestInterceptor } from 'intrig-hook';
10
+
11
+ export function getAxiosInstance(key: string) {
12
+ let axiosInstance = axios.create({
13
+ baseURL: process.env[${"`${key.toUpperCase()}_API_URL`"}],
14
+ });
15
+
16
+ axiosInstance.interceptors.request.use(requestInterceptor);
17
+
18
+ return axiosInstance;
19
+ }
20
+ `
21
+ }