@intrig/plugin-react 0.0.1 → 0.0.2-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/dist/index.cjs +4260 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4235 -0
- package/package.json +6 -3
- package/.swcrc +0 -29
- package/README.md +0 -7
- package/eslint.config.mjs +0 -19
- package/project.json +0 -29
- package/rollup.config.cjs +0 -54
- package/rollup.config.mjs +0 -33
- package/src/index.ts +0 -2
- package/src/lib/code-generator.ts +0 -79
- package/src/lib/get-endpoint-documentation.ts +0 -35
- package/src/lib/get-schema-documentation.ts +0 -11
- package/src/lib/internal-types.ts +0 -15
- package/src/lib/plugin-react.ts +0 -22
- package/src/lib/templates/context.template.ts +0 -74
- package/src/lib/templates/docs/__snapshots__/async-hook.spec.ts.snap +0 -889
- package/src/lib/templates/docs/__snapshots__/download-hook.spec.ts.snap +0 -1445
- package/src/lib/templates/docs/__snapshots__/react-hook.spec.ts.snap +0 -1371
- package/src/lib/templates/docs/__snapshots__/sse-hook.spec.ts.snap +0 -2008
- package/src/lib/templates/docs/async-hook.spec.ts +0 -92
- package/src/lib/templates/docs/async-hook.ts +0 -226
- package/src/lib/templates/docs/download-hook.spec.ts +0 -182
- package/src/lib/templates/docs/download-hook.ts +0 -170
- package/src/lib/templates/docs/react-hook.spec.ts +0 -97
- package/src/lib/templates/docs/react-hook.ts +0 -323
- package/src/lib/templates/docs/schema.ts +0 -105
- package/src/lib/templates/docs/sse-hook.spec.ts +0 -207
- package/src/lib/templates/docs/sse-hook.ts +0 -221
- package/src/lib/templates/extra.template.ts +0 -198
- package/src/lib/templates/index.template.ts +0 -14
- package/src/lib/templates/intrigMiddleware.template.ts +0 -21
- package/src/lib/templates/logger.template.ts +0 -67
- package/src/lib/templates/media-type-utils.template.ts +0 -191
- package/src/lib/templates/network-state.template.ts +0 -702
- package/src/lib/templates/packageJson.template.ts +0 -63
- package/src/lib/templates/provider/__tests__/provider-templates.spec.ts +0 -209
- package/src/lib/templates/provider/axios-config.template.ts +0 -49
- package/src/lib/templates/provider/hooks.template.ts +0 -240
- package/src/lib/templates/provider/interfaces.template.ts +0 -72
- package/src/lib/templates/provider/intrig-provider-stub.template.ts +0 -73
- package/src/lib/templates/provider/intrig-provider.template.ts +0 -185
- package/src/lib/templates/provider/main.template.ts +0 -48
- package/src/lib/templates/provider/reducer.template.ts +0 -50
- package/src/lib/templates/provider/status-trap.template.ts +0 -80
- package/src/lib/templates/provider.template.ts +0 -698
- package/src/lib/templates/source/controller/method/asyncFunctionHook.template.ts +0 -196
- package/src/lib/templates/source/controller/method/clientIndex.template.ts +0 -38
- package/src/lib/templates/source/controller/method/download.template.ts +0 -256
- package/src/lib/templates/source/controller/method/params.template.ts +0 -31
- package/src/lib/templates/source/controller/method/requestHook.template.ts +0 -220
- package/src/lib/templates/source/type/typeTemplate.ts +0 -257
- package/src/lib/templates/swcrc.template.ts +0 -25
- package/src/lib/templates/tsconfig.template.ts +0 -37
- package/src/lib/templates/type-utils.template.ts +0 -28
- package/tsconfig.json +0 -13
- package/tsconfig.lib.json +0 -20
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import {jsonLiteral} from "@intrig/plugin-sdk";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import * as fsx from "fs-extra";
|
|
4
|
-
|
|
5
|
-
export function packageJsonTemplate() {
|
|
6
|
-
const packageJson = fsx.readJsonSync(path.resolve('..', '..', 'package.json'));
|
|
7
|
-
const json = jsonLiteral(path.resolve('package.json'))
|
|
8
|
-
return json`
|
|
9
|
-
{
|
|
10
|
-
"name": "@intrig/generated",
|
|
11
|
-
"version": "d${Date.now()}",
|
|
12
|
-
"private": true,
|
|
13
|
-
"main": "dist/index.js",
|
|
14
|
-
"types": "dist/index.d.ts",
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "swc src -d dist --copy-files --strip-leading-paths && tsc --emitDeclarationOnly"
|
|
17
|
-
},
|
|
18
|
-
"dependencies": {
|
|
19
|
-
"axios": "^1.7.7",
|
|
20
|
-
"date-fns": "^4.1.0",
|
|
21
|
-
"eventsource-parser": "^3.0.2",
|
|
22
|
-
"fast-xml-parser": "^4.5.0",
|
|
23
|
-
"immer": "^10.1.1",
|
|
24
|
-
"loglevel": "1.8.1",
|
|
25
|
-
"module-alias": "^2.2.2",
|
|
26
|
-
"zod": "^3.23.8"
|
|
27
|
-
},
|
|
28
|
-
"devDependencies": {
|
|
29
|
-
"@swc/cli": "^0.7.7",
|
|
30
|
-
"@swc/core": "^1.12.6",
|
|
31
|
-
"@types/node": "^24.0.4",
|
|
32
|
-
"typescript": "${packageJson.devDependencies.typescript ?? packageJson.dependencies.typescript}",
|
|
33
|
-
"react": "${packageJson.dependencies.react}",
|
|
34
|
-
"react-dom": "${packageJson.dependencies['react-dom']}"
|
|
35
|
-
},
|
|
36
|
-
"peerDependencies": {
|
|
37
|
-
"react": "^18.0.0 || ^19.0.0",
|
|
38
|
-
"react-dom": "^18.0.0 || ^19.0.0"
|
|
39
|
-
},
|
|
40
|
-
"_moduleAliases": {
|
|
41
|
-
"@intrig/react": "./src"
|
|
42
|
-
},
|
|
43
|
-
"type": "module",
|
|
44
|
-
"exports": {
|
|
45
|
-
".": {
|
|
46
|
-
"import": "./src/index.js",
|
|
47
|
-
"require": "./src/index.js",
|
|
48
|
-
"types": "./src/index.d.ts"
|
|
49
|
-
},
|
|
50
|
-
"./*": {
|
|
51
|
-
"import": "./src/*.js",
|
|
52
|
-
"require": "./src/*.js",
|
|
53
|
-
"types": "./src/*.d.ts"
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
"typesVersions": {
|
|
57
|
-
"*": {
|
|
58
|
-
"*": ["src/*"]
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
`
|
|
63
|
-
}
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import { reactProviderInterfacesTemplate } from '../interfaces.template';
|
|
2
|
-
import { reactProviderReducerTemplate } from '../reducer.template';
|
|
3
|
-
import { reactProviderAxiosConfigTemplate } from '../axios-config.template';
|
|
4
|
-
import { reactIntrigProviderTemplate } from '../intrig-provider.template';
|
|
5
|
-
import { reactIntrigProviderStubTemplate } from '../intrig-provider-stub.template';
|
|
6
|
-
import { reactStatusTrapTemplate } from '../status-trap.template';
|
|
7
|
-
import { reactProviderHooksTemplate } from '../hooks.template';
|
|
8
|
-
import { reactProviderMainTemplate } from '../main.template';
|
|
9
|
-
import * as ts from 'typescript';
|
|
10
|
-
import * as path from 'path';
|
|
11
|
-
import * as fs from 'fs';
|
|
12
|
-
|
|
13
|
-
describe('Provider Templates', () => {
|
|
14
|
-
const mockPath = '/tmp/test-intrig';
|
|
15
|
-
const mockApisToSync = [
|
|
16
|
-
{ id: 'api1', name: 'API One' },
|
|
17
|
-
{ id: 'api2', name: 'API Two' }
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
beforeAll(() => {
|
|
21
|
-
// Create test directory structure
|
|
22
|
-
if (!fs.existsSync(path.join(mockPath, 'src'))) {
|
|
23
|
-
fs.mkdirSync(path.join(mockPath, 'src'), { recursive: true });
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
afterAll(() => {
|
|
28
|
-
// Clean up test files
|
|
29
|
-
if (fs.existsSync(mockPath)) {
|
|
30
|
-
fs.rmSync(mockPath, { recursive: true, force: true });
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
function validateTypeScriptSyntax(code: string, filename: string): void {
|
|
35
|
-
const sourceFile = ts.createSourceFile(
|
|
36
|
-
filename,
|
|
37
|
-
code,
|
|
38
|
-
ts.ScriptTarget.Latest,
|
|
39
|
-
true,
|
|
40
|
-
ts.ScriptKind.TS
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const diagnostics = ts.getPreEmitDiagnostics(
|
|
44
|
-
ts.createProgram([filename], {
|
|
45
|
-
noEmit: true,
|
|
46
|
-
skipLibCheck: true,
|
|
47
|
-
allowJs: false,
|
|
48
|
-
strict: false,
|
|
49
|
-
}, {
|
|
50
|
-
getSourceFile: (fileName) => fileName === filename ? sourceFile : undefined,
|
|
51
|
-
writeFile: () => { /* no-op for testing */ },
|
|
52
|
-
getCurrentDirectory: () => '',
|
|
53
|
-
getDirectories: () => [],
|
|
54
|
-
fileExists: () => true,
|
|
55
|
-
readFile: () => '',
|
|
56
|
-
getCanonicalFileName: (fileName) => fileName,
|
|
57
|
-
useCaseSensitiveFileNames: () => true,
|
|
58
|
-
getNewLine: () => '\n',
|
|
59
|
-
})
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const syntaxErrors = diagnostics.filter(d =>
|
|
63
|
-
d.category === ts.DiagnosticCategory.Error &&
|
|
64
|
-
d.code >= 1000 && d.code < 2000 // Syntax error codes
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
if (syntaxErrors.length > 0) {
|
|
68
|
-
const errorMessages = syntaxErrors.map(d =>
|
|
69
|
-
`${d.messageText} (${d.code})`
|
|
70
|
-
).join('\n');
|
|
71
|
-
throw new Error(`TypeScript syntax errors in ${filename}:\n${errorMessages}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
describe('reactProviderInterfacesTemplate', () => {
|
|
76
|
-
it('should generate syntactically valid TypeScript code', () => {
|
|
77
|
-
const result = reactProviderInterfacesTemplate(mockPath, mockApisToSync);
|
|
78
|
-
expect(result).toBeDefined();
|
|
79
|
-
expect(result.content).toContain('export interface DefaultConfigs');
|
|
80
|
-
expect(result.content).toContain('export interface IntrigProviderProps');
|
|
81
|
-
|
|
82
|
-
validateTypeScriptSyntax(result.content, 'interfaces.ts');
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should include all API configurations in config type', () => {
|
|
86
|
-
const result = reactProviderInterfacesTemplate(mockPath, mockApisToSync);
|
|
87
|
-
expect(result.content).toContain('api1?: DefaultConfigs');
|
|
88
|
-
expect(result.content).toContain('api2?: DefaultConfigs');
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
describe('reactProviderReducerTemplate', () => {
|
|
93
|
-
it('should generate syntactically valid TypeScript code', () => {
|
|
94
|
-
const result = reactProviderReducerTemplate(mockPath);
|
|
95
|
-
expect(result).toBeDefined();
|
|
96
|
-
expect(result.content).toContain('export function requestReducer');
|
|
97
|
-
expect(result.content).toContain('function inferNetworkReason');
|
|
98
|
-
expect(result.content).toContain('function debounce');
|
|
99
|
-
|
|
100
|
-
validateTypeScriptSyntax(result.content, 'reducer.ts');
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
describe('reactProviderAxiosConfigTemplate', () => {
|
|
105
|
-
it('should generate syntactically valid TypeScript code', () => {
|
|
106
|
-
const result = reactProviderAxiosConfigTemplate(mockPath, mockApisToSync);
|
|
107
|
-
expect(result).toBeDefined();
|
|
108
|
-
expect(result.content).toContain('export function createAxiosInstance');
|
|
109
|
-
expect(result.content).toContain('export function createAxiosInstances');
|
|
110
|
-
|
|
111
|
-
validateTypeScriptSyntax(result.content, 'axios-config.ts');
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('should include axios instances for all APIs', () => {
|
|
115
|
-
const result = reactProviderAxiosConfigTemplate(mockPath, mockApisToSync);
|
|
116
|
-
expect(result.content).toContain('api1: createAxiosInstance');
|
|
117
|
-
expect(result.content).toContain('api2: createAxiosInstance');
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe('reactIntrigProviderTemplate', () => {
|
|
122
|
-
it('should generate syntactically valid TypeScript code', () => {
|
|
123
|
-
const result = reactIntrigProviderTemplate(mockPath, mockApisToSync);
|
|
124
|
-
expect(result).toBeDefined();
|
|
125
|
-
expect(result.content).toContain('export function IntrigProvider');
|
|
126
|
-
|
|
127
|
-
validateTypeScriptSyntax(result.content, 'intrig-provider.tsx');
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe('reactIntrigProviderStubTemplate', () => {
|
|
132
|
-
it('should generate syntactically valid TypeScript code', () => {
|
|
133
|
-
const result = reactIntrigProviderStubTemplate(mockPath, mockApisToSync);
|
|
134
|
-
expect(result).toBeDefined();
|
|
135
|
-
expect(result.content).toContain('export function IntrigProviderStub');
|
|
136
|
-
|
|
137
|
-
validateTypeScriptSyntax(result.content, 'intrig-provider-stub.tsx');
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe('reactStatusTrapTemplate', () => {
|
|
142
|
-
it('should generate syntactically valid TypeScript code', () => {
|
|
143
|
-
const result = reactStatusTrapTemplate(mockPath, mockApisToSync);
|
|
144
|
-
expect(result).toBeDefined();
|
|
145
|
-
expect(result.content).toContain('export function StatusTrap');
|
|
146
|
-
|
|
147
|
-
validateTypeScriptSyntax(result.content, 'status-trap.tsx');
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
describe('reactProviderHooksTemplate', () => {
|
|
152
|
-
it('should generate syntactically valid TypeScript code', () => {
|
|
153
|
-
const result = reactProviderHooksTemplate(mockPath);
|
|
154
|
-
expect(result).toBeDefined();
|
|
155
|
-
expect(result.content).toContain('export function useNetworkState');
|
|
156
|
-
expect(result.content).toContain('export function useTransitionCall');
|
|
157
|
-
expect(result.content).toContain('export function useCentralError');
|
|
158
|
-
expect(result.content).toContain('export function useCentralPendingState');
|
|
159
|
-
|
|
160
|
-
validateTypeScriptSyntax(result.content, 'provider-hooks.ts');
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
describe('reactProviderMainTemplate', () => {
|
|
165
|
-
it('should generate syntactically valid TypeScript code', () => {
|
|
166
|
-
const result = reactProviderMainTemplate(mockPath, mockApisToSync);
|
|
167
|
-
expect(result).toBeDefined();
|
|
168
|
-
expect(result.content).toContain('export * from \'./interfaces\'');
|
|
169
|
-
expect(result.content).toContain('export * from \'./reducer\'');
|
|
170
|
-
expect(result.content).toContain('export * from \'./axios-config\'');
|
|
171
|
-
expect(result.content).toContain('export * from \'./intrig-provider\'');
|
|
172
|
-
expect(result.content).toContain('export * from \'./intrig-provider-stub\'');
|
|
173
|
-
expect(result.content).toContain('export * from \'./status-trap\'');
|
|
174
|
-
expect(result.content).toContain('export * from \'./provider-hooks\'');
|
|
175
|
-
|
|
176
|
-
validateTypeScriptSyntax(result.content, 'intrig-provider.tsx');
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
describe('Generated files integration', () => {
|
|
181
|
-
it('should generate all files and maintain consistent exports', () => {
|
|
182
|
-
const interfaces = reactProviderInterfacesTemplate(mockPath, mockApisToSync);
|
|
183
|
-
const reducer = reactProviderReducerTemplate(mockPath);
|
|
184
|
-
const axiosConfig = reactProviderAxiosConfigTemplate(mockPath, mockApisToSync);
|
|
185
|
-
const provider = reactIntrigProviderTemplate(mockPath, mockApisToSync);
|
|
186
|
-
const providerStub = reactIntrigProviderStubTemplate(mockPath, mockApisToSync);
|
|
187
|
-
const statusTrap = reactStatusTrapTemplate(mockPath, mockApisToSync);
|
|
188
|
-
const hooks = reactProviderHooksTemplate(mockPath);
|
|
189
|
-
const main = reactProviderMainTemplate(mockPath, mockApisToSync);
|
|
190
|
-
|
|
191
|
-
// Verify all templates generate valid content
|
|
192
|
-
expect(interfaces.content).toBeTruthy();
|
|
193
|
-
expect(reducer.content).toBeTruthy();
|
|
194
|
-
expect(axiosConfig.content).toBeTruthy();
|
|
195
|
-
expect(provider.content).toBeTruthy();
|
|
196
|
-
expect(providerStub.content).toBeTruthy();
|
|
197
|
-
expect(statusTrap.content).toBeTruthy();
|
|
198
|
-
expect(hooks.content).toBeTruthy();
|
|
199
|
-
expect(main.content).toBeTruthy();
|
|
200
|
-
|
|
201
|
-
// Verify main template re-exports all modules
|
|
202
|
-
expect(main.content).toContain('IntrigProvider');
|
|
203
|
-
expect(main.content).toContain('useNetworkState');
|
|
204
|
-
expect(main.content).toContain('requestReducer');
|
|
205
|
-
expect(main.content).toContain('createAxiosInstance');
|
|
206
|
-
expect(main.content).toContain('DefaultConfigs');
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
});
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import {IntrigSourceConfig, typescript} from "@intrig/plugin-sdk";
|
|
2
|
-
import * as path from 'path'
|
|
3
|
-
|
|
4
|
-
export function providerAxiosConfigTemplate(apisToSync: IntrigSourceConfig[]) {
|
|
5
|
-
const axiosConfigs = apisToSync.map(a => `
|
|
6
|
-
${a.id}: createAxiosInstance(configs.defaults, configs['${a.id}']),
|
|
7
|
-
`).join("\n");
|
|
8
|
-
|
|
9
|
-
const ts = typescript(path.resolve("src", "axios-config.ts"))
|
|
10
|
-
return ts`import axios, {
|
|
11
|
-
Axios,
|
|
12
|
-
AxiosResponse,
|
|
13
|
-
InternalAxiosRequestConfig,
|
|
14
|
-
} from 'axios';
|
|
15
|
-
import { DefaultConfigs } from './interfaces';
|
|
16
|
-
|
|
17
|
-
export function createAxiosInstance(
|
|
18
|
-
defaultConfig?: DefaultConfigs,
|
|
19
|
-
config?: DefaultConfigs,
|
|
20
|
-
) {
|
|
21
|
-
const axiosInstance = axios.create({
|
|
22
|
-
...(defaultConfig ?? {}),
|
|
23
|
-
...(config ?? {}),
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
async function requestInterceptor(cfg: InternalAxiosRequestConfig) {
|
|
27
|
-
const intermediate = (await defaultConfig?.requestInterceptor?.(cfg)) ?? cfg;
|
|
28
|
-
return config?.requestInterceptor?.(intermediate) ?? intermediate;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function responseInterceptor(cfg: AxiosResponse<any>) {
|
|
32
|
-
const intermediate = (await defaultConfig?.responseInterceptor?.(cfg)) ?? cfg;
|
|
33
|
-
return config?.responseInterceptor?.(intermediate) ?? intermediate;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
axiosInstance.interceptors.request.use(requestInterceptor);
|
|
37
|
-
axiosInstance.interceptors.response.use(responseInterceptor, (error) => {
|
|
38
|
-
return Promise.reject(error);
|
|
39
|
-
});
|
|
40
|
-
return axiosInstance;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function createAxiosInstances(configs: any): Record<string, Axios> {
|
|
44
|
-
return {
|
|
45
|
-
${axiosConfigs}
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
`
|
|
49
|
-
}
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import {typescript} from "@intrig/plugin-sdk";
|
|
2
|
-
import * as path from 'path'
|
|
3
|
-
|
|
4
|
-
export function providerHooksTemplate() {
|
|
5
|
-
const ts = typescript(path.resolve("src", "provider-hooks.ts"))
|
|
6
|
-
return ts`import React, {
|
|
7
|
-
useCallback,
|
|
8
|
-
useContext,
|
|
9
|
-
useMemo,
|
|
10
|
-
useState,
|
|
11
|
-
useRef,
|
|
12
|
-
} from 'react';
|
|
13
|
-
import {
|
|
14
|
-
ErrorState,
|
|
15
|
-
ErrorWithContext,
|
|
16
|
-
isSuccess,
|
|
17
|
-
isError,
|
|
18
|
-
isPending,
|
|
19
|
-
NetworkState,
|
|
20
|
-
pending,
|
|
21
|
-
Progress,
|
|
22
|
-
init,
|
|
23
|
-
} from './network-state';
|
|
24
|
-
import {
|
|
25
|
-
AxiosProgressEvent,
|
|
26
|
-
} from 'axios';
|
|
27
|
-
import { ZodSchema } from 'zod';
|
|
28
|
-
import logger from './logger';
|
|
29
|
-
|
|
30
|
-
import { Context, RequestType, SchemaOf } from './intrig-context';
|
|
31
|
-
import { NetworkStateProps } from './interfaces';
|
|
32
|
-
import { debounce } from './reducer';
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* useNetworkState is a custom hook that manages the network state within the specified context.
|
|
36
|
-
* It handles making network requests, dispatching appropriate states based on the request lifecycle,
|
|
37
|
-
* and allows aborting ongoing requests.
|
|
38
|
-
*/
|
|
39
|
-
export function useNetworkState<T>({
|
|
40
|
-
key,
|
|
41
|
-
operation,
|
|
42
|
-
source,
|
|
43
|
-
schema,
|
|
44
|
-
errorSchema,
|
|
45
|
-
debounceDelay: requestDebounceDelay,
|
|
46
|
-
}: NetworkStateProps<T>): [
|
|
47
|
-
NetworkState<T>,
|
|
48
|
-
(request: RequestType) => void,
|
|
49
|
-
() => void,
|
|
50
|
-
(state: NetworkState<T>) => void,
|
|
51
|
-
] {
|
|
52
|
-
const context = useContext(Context);
|
|
53
|
-
|
|
54
|
-
const [abortController, setAbortController] = useState<AbortController>();
|
|
55
|
-
|
|
56
|
-
const networkState = useMemo(() => {
|
|
57
|
-
logger.info(${"`Updating status ${key} ${operation} ${source}`"});
|
|
58
|
-
logger.debug("<=", context.state?.[${"`${source}:${operation}:${key}`"}])
|
|
59
|
-
return (
|
|
60
|
-
(context.state?.[${"`${source}:${operation}:${key}`"}] as NetworkState<T>) ??
|
|
61
|
-
init()
|
|
62
|
-
);
|
|
63
|
-
}, [JSON.stringify(context.state?.[${"`${source}:${operation}:${key}`"}])]);
|
|
64
|
-
|
|
65
|
-
const dispatch = useCallback(
|
|
66
|
-
(state: NetworkState<T>) => {
|
|
67
|
-
context.dispatch({ key, operation, source, state });
|
|
68
|
-
},
|
|
69
|
-
[key, operation, source, context.dispatch],
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
const debounceDelay = useMemo(() => {
|
|
73
|
-
return (
|
|
74
|
-
requestDebounceDelay ?? context.configs?.[source as keyof (typeof context)['configs']]?.debounceDelay ?? 0
|
|
75
|
-
);
|
|
76
|
-
}, [context.configs, requestDebounceDelay, source]);
|
|
77
|
-
|
|
78
|
-
const execute = useCallback(
|
|
79
|
-
async (request: RequestType) => {
|
|
80
|
-
logger.info(${"`Executing request ${key} ${operation} ${source}`"});
|
|
81
|
-
logger.debug("=>", request)
|
|
82
|
-
|
|
83
|
-
const abortController = new AbortController();
|
|
84
|
-
setAbortController(abortController);
|
|
85
|
-
|
|
86
|
-
const requestConfig: RequestType = {
|
|
87
|
-
...request,
|
|
88
|
-
onUploadProgress(event: AxiosProgressEvent) {
|
|
89
|
-
dispatch(
|
|
90
|
-
pending({
|
|
91
|
-
type: 'upload',
|
|
92
|
-
loaded: event.loaded,
|
|
93
|
-
total: event.total,
|
|
94
|
-
}),
|
|
95
|
-
);
|
|
96
|
-
request.onUploadProgress?.(event);
|
|
97
|
-
},
|
|
98
|
-
onDownloadProgress(event: AxiosProgressEvent) {
|
|
99
|
-
dispatch(
|
|
100
|
-
pending({
|
|
101
|
-
type: 'download',
|
|
102
|
-
loaded: event.loaded,
|
|
103
|
-
total: event.total,
|
|
104
|
-
}),
|
|
105
|
-
);
|
|
106
|
-
request.onDownloadProgress?.(event);
|
|
107
|
-
},
|
|
108
|
-
signal: abortController.signal,
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
await context.execute(
|
|
112
|
-
requestConfig,
|
|
113
|
-
dispatch,
|
|
114
|
-
schema,
|
|
115
|
-
errorSchema as any,
|
|
116
|
-
);
|
|
117
|
-
},
|
|
118
|
-
[networkState, context.dispatch],
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
const deboundedExecute = useMemo(
|
|
122
|
-
() => debounce(execute, debounceDelay ?? 0),
|
|
123
|
-
[execute],
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
const clear = useCallback(() => {
|
|
127
|
-
logger.info(${"`Clearing request ${key} ${operation} ${source}`"});
|
|
128
|
-
dispatch(init());
|
|
129
|
-
setAbortController((abortController) => {
|
|
130
|
-
logger.info(${"`Aborting request ${key} ${operation} ${source}`"});
|
|
131
|
-
abortController?.abort();
|
|
132
|
-
return undefined;
|
|
133
|
-
});
|
|
134
|
-
}, [dispatch, abortController]);
|
|
135
|
-
|
|
136
|
-
return [networkState, deboundedExecute, clear, dispatch];
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* A hook for making transient calls that can be aborted and validated against schemas.
|
|
141
|
-
*/
|
|
142
|
-
export function useTransitionCall<T>({
|
|
143
|
-
schema,
|
|
144
|
-
errorSchema,
|
|
145
|
-
}: {
|
|
146
|
-
schema?: SchemaOf<T>;
|
|
147
|
-
errorSchema?: SchemaOf<T>;
|
|
148
|
-
}): [(request: RequestType) => Promise<T>, () => void] {
|
|
149
|
-
const ctx = useContext(Context);
|
|
150
|
-
const controller = useRef<AbortController | undefined>(undefined);
|
|
151
|
-
|
|
152
|
-
const call = useCallback(
|
|
153
|
-
async (request: RequestType) => {
|
|
154
|
-
controller.current?.abort();
|
|
155
|
-
const abort = new AbortController();
|
|
156
|
-
controller.current = abort;
|
|
157
|
-
|
|
158
|
-
return new Promise<T>((resolve, reject) => {
|
|
159
|
-
ctx.execute(
|
|
160
|
-
{ ...request, signal: abort.signal },
|
|
161
|
-
(state) => {
|
|
162
|
-
if (isSuccess(state)) {
|
|
163
|
-
resolve(state.data as T);
|
|
164
|
-
} else if (isError(state)) {
|
|
165
|
-
reject(state.error);
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
schema,
|
|
169
|
-
errorSchema,
|
|
170
|
-
);
|
|
171
|
-
});
|
|
172
|
-
},
|
|
173
|
-
[ctx, schema, errorSchema],
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
const abort = useCallback(() => {
|
|
177
|
-
controller.current?.abort();
|
|
178
|
-
}, []);
|
|
179
|
-
|
|
180
|
-
return [call, abort];
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Handles central error extraction from the provided context.
|
|
185
|
-
* It filters the state to retain error states and maps them to a structured error object with additional context information.
|
|
186
|
-
* @return {Object[]} An array of objects representing the error states with context information such as source, operation, and key.
|
|
187
|
-
*/
|
|
188
|
-
export function useCentralError() {
|
|
189
|
-
const ctx = useContext(Context);
|
|
190
|
-
|
|
191
|
-
return useMemo(() => {
|
|
192
|
-
return Object.entries(ctx.filteredState as Record<string, NetworkState>)
|
|
193
|
-
.filter(([, state]) => isError(state))
|
|
194
|
-
.map(([k, state]) => {
|
|
195
|
-
const [source, operation, key] = k.split(':');
|
|
196
|
-
return {
|
|
197
|
-
...(state as ErrorState<unknown>),
|
|
198
|
-
source,
|
|
199
|
-
operation,
|
|
200
|
-
key,
|
|
201
|
-
} satisfies ErrorWithContext;
|
|
202
|
-
});
|
|
203
|
-
}, [ctx.filteredState]);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Uses central pending state handling by aggregating pending states from context.
|
|
208
|
-
* It calculates the overall progress of pending states if any, or returns an initial state otherwise.
|
|
209
|
-
*
|
|
210
|
-
* @return {NetworkState} The aggregated network state based on the pending states and their progress.
|
|
211
|
-
*/
|
|
212
|
-
export function useCentralPendingState() {
|
|
213
|
-
const ctx = useContext(Context);
|
|
214
|
-
|
|
215
|
-
const result: NetworkState = useMemo(() => {
|
|
216
|
-
const pendingStates = Object.values(
|
|
217
|
-
ctx.filteredState as Record<string, NetworkState>,
|
|
218
|
-
).filter(isPending);
|
|
219
|
-
if (!pendingStates.length) {
|
|
220
|
-
return init();
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const progress = pendingStates
|
|
224
|
-
.filter((a) => a.progress)
|
|
225
|
-
.reduce(
|
|
226
|
-
(progress, current) => {
|
|
227
|
-
return {
|
|
228
|
-
total: progress.total + (current.progress?.total ?? 0),
|
|
229
|
-
loaded: progress.loaded + (current.progress?.loaded ?? 0),
|
|
230
|
-
};
|
|
231
|
-
},
|
|
232
|
-
{ total: 0, loaded: 0 } satisfies Progress,
|
|
233
|
-
);
|
|
234
|
-
return pending(progress.total ? progress : undefined);
|
|
235
|
-
}, [ctx.filteredState]);
|
|
236
|
-
|
|
237
|
-
return result;
|
|
238
|
-
}
|
|
239
|
-
`
|
|
240
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import {IntrigSourceConfig, typescript} from "@intrig/plugin-sdk";
|
|
2
|
-
import * as path from 'path'
|
|
3
|
-
|
|
4
|
-
export function providerInterfacesTemplate(apisToSync: IntrigSourceConfig[]) {
|
|
5
|
-
const configType = `{
|
|
6
|
-
defaults?: DefaultConfigs,
|
|
7
|
-
${apisToSync.map(a => `${a.id}?: DefaultConfigs`).join(",\n ")}
|
|
8
|
-
}`
|
|
9
|
-
|
|
10
|
-
const ts = typescript(path.resolve("src", "interfaces.ts"))
|
|
11
|
-
return ts`import {
|
|
12
|
-
CreateAxiosDefaults,
|
|
13
|
-
InternalAxiosRequestConfig,
|
|
14
|
-
AxiosResponse,
|
|
15
|
-
} from 'axios';
|
|
16
|
-
import {
|
|
17
|
-
IntrigHook,
|
|
18
|
-
NetworkState,
|
|
19
|
-
} from './network-state';
|
|
20
|
-
import { SchemaOf } from './intrig-context';
|
|
21
|
-
|
|
22
|
-
export interface DefaultConfigs extends CreateAxiosDefaults {
|
|
23
|
-
debounceDelay?: number;
|
|
24
|
-
requestInterceptor?: (
|
|
25
|
-
config: InternalAxiosRequestConfig,
|
|
26
|
-
) => Promise<InternalAxiosRequestConfig>;
|
|
27
|
-
responseInterceptor?: (
|
|
28
|
-
config: AxiosResponse<any>,
|
|
29
|
-
) => Promise<AxiosResponse<any>>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface IntrigProviderProps {
|
|
33
|
-
configs?: ${configType};
|
|
34
|
-
children: React.ReactNode;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface StubType {
|
|
38
|
-
<P, B, T>(
|
|
39
|
-
hook: IntrigHook<P, B, T>,
|
|
40
|
-
fn: (
|
|
41
|
-
params: P,
|
|
42
|
-
body: B,
|
|
43
|
-
dispatch: (state: NetworkState<T>) => void,
|
|
44
|
-
) => Promise<void>,
|
|
45
|
-
): void;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export type WithStubSupport<T> = T & {
|
|
49
|
-
stubs?: (stub: StubType) => void;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export interface IntrigProviderStubProps {
|
|
53
|
-
configs?: ${configType};
|
|
54
|
-
stubs?: (stub: StubType) => void;
|
|
55
|
-
children: React.ReactNode;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface StatusTrapProps {
|
|
59
|
-
type: 'pending' | 'error' | 'pending + error';
|
|
60
|
-
propagate?: boolean;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export interface NetworkStateProps<T> {
|
|
64
|
-
key: string;
|
|
65
|
-
operation: string;
|
|
66
|
-
source: string;
|
|
67
|
-
schema?: SchemaOf<T>;
|
|
68
|
-
errorSchema?: SchemaOf<any>;
|
|
69
|
-
debounceDelay?: number;
|
|
70
|
-
}
|
|
71
|
-
`
|
|
72
|
-
}
|