@bagelink/sdk 1.8.28 → 1.8.32
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 +76 -45
- package/dist/index.cjs +425 -12
- package/dist/index.d.cts +141 -2
- package/dist/index.d.mts +141 -2
- package/dist/index.d.ts +141 -2
- package/dist/index.mjs +420 -13
- package/package.json +8 -3
- package/src/index.ts +412 -1
- package/src/openAPITools/functionGenerator.ts +193 -16
package/README.md
CHANGED
|
@@ -1,64 +1,95 @@
|
|
|
1
|
-
# @bagelink/
|
|
1
|
+
# @bagelink/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
OpenAPI-based TypeScript SDK generator with Vue 3 composables.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- File-to-file transpilation via [mkdist](https://github.com/unjs/mkdist)
|
|
7
|
-
- Playground with [vite](https://vitejs.dev/)
|
|
5
|
+
## Features
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
- 🚀 Generate type-safe SDKs from OpenAPI specifications
|
|
8
|
+
- 🎯 Enhanced responses - array + metadata in one object
|
|
9
|
+
- 🔐 Built-in authentication support
|
|
10
|
+
- 🎨 FastAPI/Pydantic error parsing
|
|
11
|
+
- 📡 SSE/Stream support with type safety
|
|
12
|
+
- ⚡ Request state management composables
|
|
13
|
+
- 🔧 Flexible configuration with interceptors
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
## Installation
|
|
12
16
|
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import { BagelVue } from '@bagel/vue';
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @bagelink/sdk
|
|
19
|
+
```
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
## Quick Start
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
};
|
|
23
|
+
### 1. Generate SDK
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# From OpenAPI spec
|
|
27
|
+
VITE_BAGEL_BASE_URL=https://api.example.com bun run bin/index.ts .bagelink
|
|
28
|
+
```
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
app.use(head as Plugin);
|
|
30
|
+
### 2. Initialize
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
```typescript
|
|
33
|
+
import { createApi } from './.bagelink/api'
|
|
34
|
+
|
|
35
|
+
createApi({
|
|
36
|
+
baseURL: '/api',
|
|
37
|
+
withCredentials: true
|
|
38
|
+
})
|
|
33
39
|
```
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
### 3. Use
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { useApi } from './.bagelink/api'
|
|
45
|
+
|
|
46
|
+
const api = useApi()
|
|
47
|
+
const users = await api.users.get()
|
|
36
48
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
// Array operations
|
|
50
|
+
for (const user of users) {
|
|
51
|
+
console.log(user.name)
|
|
52
|
+
}
|
|
40
53
|
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
// Metadata
|
|
55
|
+
console.log(users.totalCount, users.page)
|
|
43
56
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
// Raw access
|
|
58
|
+
console.log(users.$raw.headers)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Configuration
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
createApi({
|
|
65
|
+
baseURL: '/api',
|
|
66
|
+
withCredentials: true,
|
|
67
|
+
auth: {
|
|
68
|
+
getToken: () => localStorage.getItem('token'),
|
|
69
|
+
onUnauthorized: () => router.push('/login')
|
|
49
70
|
},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
onError: (error) => {
|
|
72
|
+
console.error(error.message)
|
|
73
|
+
if (error.fields) {
|
|
74
|
+
// Handle validation errors
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
interceptors: {
|
|
78
|
+
request: (config) => {
|
|
79
|
+
// Modify request
|
|
80
|
+
return config
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
})
|
|
59
84
|
```
|
|
60
85
|
|
|
86
|
+
## State Management
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { useApiRequest } from './.bagelink/api'
|
|
61
90
|
|
|
62
|
-
|
|
91
|
+
const { loading, error, data, execute } = useApiRequest(
|
|
92
|
+
() => api.users.get()
|
|
93
|
+
)
|
|
94
|
+
```
|
|
63
95
|
|
|
64
|
-
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const axios$1 = require('axios');
|
|
4
|
+
const vue = require('vue');
|
|
4
5
|
|
|
5
6
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
6
7
|
|
|
@@ -638,7 +639,11 @@ function generateAxiosFunction(method, formattedPath, allParams, responseTypeStr
|
|
|
638
639
|
if (allParams === "undefined") {
|
|
639
640
|
allParams = "";
|
|
640
641
|
}
|
|
641
|
-
|
|
642
|
+
const apiResponseType = responseTypeStr.replace(
|
|
643
|
+
/Promise<AxiosResponse<(.+)>>/,
|
|
644
|
+
"Promise<ApiResponse<$1>>"
|
|
645
|
+
);
|
|
646
|
+
let axiosFunction = `async (${allParams})${apiResponseType} => {`;
|
|
642
647
|
const paramStr = parameters.config?.params ? `params: {${parameters.config.params}}` : "";
|
|
643
648
|
if (requestBodyPayload === "formData") {
|
|
644
649
|
if (allParams.includes("file: File")) {
|
|
@@ -646,20 +651,24 @@ function generateAxiosFunction(method, formattedPath, allParams, responseTypeStr
|
|
|
646
651
|
const formData = new FormData()
|
|
647
652
|
formData.append('file', file)
|
|
648
653
|
formData.append('upload', file)
|
|
649
|
-
|
|
654
|
+
const response = await axios.${method}(${formattedPath}, formData, {
|
|
650
655
|
headers: { 'Content-Type': 'multipart/form-data' },
|
|
651
656
|
onUploadProgress: options?.onUploadProgress${paramStr ? `, ${paramStr}` : ""}
|
|
652
|
-
})
|
|
657
|
+
})
|
|
658
|
+
return ApiResponse.create(response.data, response)`;
|
|
653
659
|
} else {
|
|
654
660
|
axiosFunction += `
|
|
655
|
-
|
|
661
|
+
const response = await axios.${method}(${formattedPath}, formData, {
|
|
656
662
|
headers: { 'Content-Type': 'multipart/form-data' }${paramStr ? `, ${paramStr}` : ""}
|
|
657
|
-
})
|
|
663
|
+
})
|
|
664
|
+
return ApiResponse.create(response.data, response)`;
|
|
658
665
|
}
|
|
659
666
|
} else {
|
|
660
667
|
const configParams = paramStr ? `, { ${paramStr} }` : "";
|
|
661
668
|
const bodyVar = requestBodyPayload || "{}";
|
|
662
|
-
axiosFunction += `
|
|
669
|
+
axiosFunction += `
|
|
670
|
+
const response = await axios.${method}(${formattedPath}${["get", "delete"].includes(method) ? configParams : `, ${bodyVar}${configParams}`})
|
|
671
|
+
return ApiResponse.create(response.data, response)`;
|
|
663
672
|
}
|
|
664
673
|
axiosFunction += "}";
|
|
665
674
|
return axiosFunction;
|
|
@@ -818,6 +827,7 @@ function generateFunctions(paths, baseUrl) {
|
|
|
818
827
|
}, body);
|
|
819
828
|
}
|
|
820
829
|
let tsString = "";
|
|
830
|
+
const apiObjectEntries = [];
|
|
821
831
|
for (const [parent, object] of Object.entries(body)) {
|
|
822
832
|
tsString += `
|
|
823
833
|
/**
|
|
@@ -825,7 +835,16 @@ function generateFunctions(paths, baseUrl) {
|
|
|
825
835
|
*/
|
|
826
836
|
export const ${parent} = ${JSON.stringify(object, void 0, 2)};
|
|
827
837
|
`;
|
|
838
|
+
apiObjectEntries.push(parent);
|
|
828
839
|
}
|
|
840
|
+
tsString += `
|
|
841
|
+
/**
|
|
842
|
+
* Aggregated API object containing all endpoints
|
|
843
|
+
*/
|
|
844
|
+
export const api = {
|
|
845
|
+
${apiObjectEntries.map((name) => ` ${name},`).join("\n")}
|
|
846
|
+
};
|
|
847
|
+
`;
|
|
829
848
|
Object.entries(functionsInventory).forEach(([key, value]) => {
|
|
830
849
|
tsString = tsString.replace(`"${key}"`, value);
|
|
831
850
|
});
|
|
@@ -901,11 +920,27 @@ function generateStreamEventTypeDefinitions() {
|
|
|
901
920
|
function fileTemplate(tsString, typeForImport, baseURL) {
|
|
902
921
|
const streamTypeDefs = generateStreamEventTypeDefinitions();
|
|
903
922
|
const hasStreamEndpoints = Object.keys(streamEventTypes).length > 0;
|
|
904
|
-
const streamImports = hasStreamEndpoints ? `
|
|
923
|
+
const streamImports = hasStreamEndpoints ? `createSSEStreamPost, createSSEStream, StreamController, type SSEStreamOptions,` : "";
|
|
905
924
|
const templateCode = `import ax from 'axios';
|
|
906
925
|
import type { AxiosResponse } from 'axios';
|
|
907
926
|
import type { ${typeForImport.join(", ")} } from './types.d';
|
|
908
|
-
|
|
927
|
+
import {
|
|
928
|
+
ApiResponse,
|
|
929
|
+
parseApiError,
|
|
930
|
+
formatFieldErrors,
|
|
931
|
+
useApiRequest,
|
|
932
|
+
useCancellableRequest,
|
|
933
|
+
${streamImports}
|
|
934
|
+
type ApiConfig,
|
|
935
|
+
type ParsedError,
|
|
936
|
+
type FastAPIValidationError,
|
|
937
|
+
type FastAPIErrorResponse,
|
|
938
|
+
type ResponseMetadata,
|
|
939
|
+
type ApiRequestState,
|
|
940
|
+
type RequestInterceptor,
|
|
941
|
+
type ResponseInterceptor
|
|
942
|
+
} from '@bagelink/sdk';
|
|
943
|
+
import { ref, type Ref } from 'vue';
|
|
909
944
|
|
|
910
945
|
/**
|
|
911
946
|
* Options for file upload operations
|
|
@@ -920,14 +955,154 @@ export interface UploadOptions {
|
|
|
920
955
|
}
|
|
921
956
|
|
|
922
957
|
${streamTypeDefs}
|
|
958
|
+
|
|
923
959
|
/**
|
|
924
960
|
* Configured axios instance for API requests
|
|
925
|
-
* @example
|
|
926
|
-
* // Making a direct request with the axios instance
|
|
927
|
-
* const response = await axios.get('/some-endpoint');
|
|
928
961
|
*/
|
|
929
962
|
export const axios = ax.create({baseURL: '${baseURL}', withCredentials: true});
|
|
930
|
-
|
|
963
|
+
|
|
964
|
+
${tsString}
|
|
965
|
+
|
|
966
|
+
// ============================================================================
|
|
967
|
+
// API Instance & Configuration
|
|
968
|
+
// ============================================================================
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* Last response metadata (accessible via useApi().$lastResponse)
|
|
972
|
+
*/
|
|
973
|
+
const lastResponse = ref<AxiosResponse | null>(null)
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* API instance type with enhanced response objects
|
|
977
|
+
*/
|
|
978
|
+
type ApiInstance = typeof api & {
|
|
979
|
+
$raw: typeof api
|
|
980
|
+
$lastResponse: Ref<AxiosResponse | null>
|
|
981
|
+
$axios: typeof axios
|
|
982
|
+
$ApiResponse: typeof ApiResponse
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// Global state
|
|
986
|
+
let apiConfig: ApiConfig | null = null
|
|
987
|
+
let apiInstance: ApiInstance | null = null
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Create and configure the API instance
|
|
991
|
+
* @param config - API configuration options
|
|
992
|
+
* @returns Configured API instance
|
|
993
|
+
*/
|
|
994
|
+
export function createApi(config: ApiConfig = {}): ApiInstance {
|
|
995
|
+
// Store config
|
|
996
|
+
apiConfig = config
|
|
997
|
+
|
|
998
|
+
// Configure axios defaults
|
|
999
|
+
if (config.baseURL) {
|
|
1000
|
+
axios.defaults.baseURL = config.baseURL
|
|
1001
|
+
}
|
|
1002
|
+
if (config.withCredentials !== undefined) {
|
|
1003
|
+
axios.defaults.withCredentials = config.withCredentials
|
|
1004
|
+
}
|
|
1005
|
+
if (config.axiosConfig) {
|
|
1006
|
+
Object.assign(axios.defaults, config.axiosConfig)
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// Clear existing interceptors
|
|
1010
|
+
axios.interceptors.request.clear()
|
|
1011
|
+
axios.interceptors.response.clear()
|
|
1012
|
+
|
|
1013
|
+
// Setup auth interceptor
|
|
1014
|
+
if (config.auth) {
|
|
1015
|
+
axios.interceptors.request.use(async (reqConfig) => {
|
|
1016
|
+
const token = await config.auth!.getToken?.()
|
|
1017
|
+
if (token) {
|
|
1018
|
+
const prefix = config.auth!.tokenPrefix ?? 'Bearer'
|
|
1019
|
+
reqConfig.headers.Authorization = \`\${prefix} \${token}\`
|
|
1020
|
+
}
|
|
1021
|
+
return reqConfig
|
|
1022
|
+
})
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// Setup custom request interceptor
|
|
1026
|
+
if (config.interceptors?.request) {
|
|
1027
|
+
axios.interceptors.request.use(config.interceptors.request)
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// Setup response interceptor for error handling
|
|
1031
|
+
axios.interceptors.response.use(
|
|
1032
|
+
(response) => {
|
|
1033
|
+
// Store last response for metadata access
|
|
1034
|
+
lastResponse.value = response
|
|
1035
|
+
|
|
1036
|
+
// Apply custom response interceptor if provided
|
|
1037
|
+
if (config.interceptors?.response) {
|
|
1038
|
+
return config.interceptors.response(response)
|
|
1039
|
+
}
|
|
1040
|
+
return response
|
|
1041
|
+
},
|
|
1042
|
+
async (error) => {
|
|
1043
|
+
// Handle 401 Unauthorized
|
|
1044
|
+
if (error.response?.status === 401) {
|
|
1045
|
+
await config.auth?.onUnauthorized?.()
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// Parse and handle error
|
|
1049
|
+
const parsedError = parseApiError(error)
|
|
1050
|
+
config.onError?.(parsedError, error)
|
|
1051
|
+
|
|
1052
|
+
throw error
|
|
1053
|
+
}
|
|
1054
|
+
)
|
|
1055
|
+
|
|
1056
|
+
// API already returns ApiResponse directly!
|
|
1057
|
+
// Just add the special properties
|
|
1058
|
+
apiInstance = {
|
|
1059
|
+
...api,
|
|
1060
|
+
$raw: api,
|
|
1061
|
+
$lastResponse: lastResponse,
|
|
1062
|
+
$axios: axios,
|
|
1063
|
+
$ApiResponse: ApiResponse
|
|
1064
|
+
} as ApiInstance
|
|
1065
|
+
|
|
1066
|
+
return apiInstance
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* Access the configured API instance
|
|
1071
|
+
* @returns API instance with enhanced response objects
|
|
1072
|
+
* @throws Error if createApi hasn't been called
|
|
1073
|
+
*/
|
|
1074
|
+
export function useApi(): ApiInstance {
|
|
1075
|
+
if (!apiInstance) {
|
|
1076
|
+
throw new Error(
|
|
1077
|
+
'API not initialized. Call createApi() first.\\n\\n' +
|
|
1078
|
+
'Example:\\n' +
|
|
1079
|
+
'import { createApi } from \\'./generated/api\\'\\n' +
|
|
1080
|
+
'createApi({ baseURL: \\'/api\\' })'
|
|
1081
|
+
)
|
|
1082
|
+
}
|
|
1083
|
+
return apiInstance
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// ============================================================================
|
|
1087
|
+
// Re-export utilities for convenience
|
|
1088
|
+
// ============================================================================
|
|
1089
|
+
|
|
1090
|
+
export {
|
|
1091
|
+
parseApiError,
|
|
1092
|
+
formatFieldErrors,
|
|
1093
|
+
useApiRequest,
|
|
1094
|
+
useCancellableRequest,
|
|
1095
|
+
ApiResponse,
|
|
1096
|
+
type ApiConfig,
|
|
1097
|
+
type ParsedError,
|
|
1098
|
+
type ResponseMetadata,
|
|
1099
|
+
type ApiRequestState,
|
|
1100
|
+
type FastAPIValidationError,
|
|
1101
|
+
type FastAPIErrorResponse,
|
|
1102
|
+
type RequestInterceptor,
|
|
1103
|
+
type ResponseInterceptor
|
|
1104
|
+
}
|
|
1105
|
+
`;
|
|
931
1106
|
return templateCode.replace(/"([^"]+)":/g, "$1:");
|
|
932
1107
|
}
|
|
933
1108
|
|
|
@@ -1249,6 +1424,238 @@ function formatAPIErrorMessage(err) {
|
|
|
1249
1424
|
return `An error occurred (Status ${status}): ${error.message || "Unknown error"}`;
|
|
1250
1425
|
}
|
|
1251
1426
|
|
|
1427
|
+
class ApiResponse extends Array {
|
|
1428
|
+
/** Raw axios response */
|
|
1429
|
+
$raw;
|
|
1430
|
+
/** Total number of items (from x-total-count header) */
|
|
1431
|
+
totalCount;
|
|
1432
|
+
/** Current page number (from x-page header) */
|
|
1433
|
+
page;
|
|
1434
|
+
/** Items per page (from x-per-page header) */
|
|
1435
|
+
perPage;
|
|
1436
|
+
/** Total number of pages (calculated) */
|
|
1437
|
+
totalPages;
|
|
1438
|
+
/** Rate limit remaining (from x-ratelimit-remaining header) */
|
|
1439
|
+
rateLimit;
|
|
1440
|
+
/** Rate limit reset timestamp (from x-ratelimit-reset header) */
|
|
1441
|
+
rateLimitReset;
|
|
1442
|
+
/** All response metadata */
|
|
1443
|
+
$metadata;
|
|
1444
|
+
constructor(data, response) {
|
|
1445
|
+
super();
|
|
1446
|
+
if (Array.isArray(data)) {
|
|
1447
|
+
this.push(...data);
|
|
1448
|
+
if (response) {
|
|
1449
|
+
this.$raw = response;
|
|
1450
|
+
this.$metadata = ApiResponse.parseMetadataStatic(response);
|
|
1451
|
+
this.totalCount = this.$metadata.totalCount;
|
|
1452
|
+
this.page = this.$metadata.page;
|
|
1453
|
+
this.perPage = this.$metadata.perPage;
|
|
1454
|
+
this.totalPages = this.$metadata.totalPages;
|
|
1455
|
+
this.rateLimit = this.$metadata.rateLimit;
|
|
1456
|
+
this.rateLimitReset = this.$metadata.rateLimitReset;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* Create an ApiResponse from data and response
|
|
1462
|
+
* Factory method to handle both arrays and objects
|
|
1463
|
+
*/
|
|
1464
|
+
static create(data, response) {
|
|
1465
|
+
if (!Array.isArray(data)) {
|
|
1466
|
+
const obj = Object.assign(/* @__PURE__ */ Object.create(null), data);
|
|
1467
|
+
obj.$raw = response;
|
|
1468
|
+
obj.$metadata = ApiResponse.parseMetadataStatic(response);
|
|
1469
|
+
obj.totalCount = obj.$metadata.totalCount;
|
|
1470
|
+
obj.page = obj.$metadata.page;
|
|
1471
|
+
obj.perPage = obj.$metadata.perPage;
|
|
1472
|
+
obj.totalPages = obj.$metadata.totalPages;
|
|
1473
|
+
obj.rateLimit = obj.$metadata.rateLimit;
|
|
1474
|
+
obj.rateLimitReset = obj.$metadata.rateLimitReset;
|
|
1475
|
+
obj.getHeader = (name) => response?.headers[name.toLowerCase()];
|
|
1476
|
+
obj.hasPagination = obj.totalCount !== void 0 || obj.page !== void 0;
|
|
1477
|
+
obj.hasNextPage = obj.page && obj.totalPages ? obj.page < obj.totalPages : false;
|
|
1478
|
+
obj.hasPrevPage = obj.page ? obj.page > 1 : false;
|
|
1479
|
+
return obj;
|
|
1480
|
+
}
|
|
1481
|
+
const instance = new ApiResponse(data, response);
|
|
1482
|
+
return instance;
|
|
1483
|
+
}
|
|
1484
|
+
static parseMetadataStatic(response) {
|
|
1485
|
+
const { headers } = response;
|
|
1486
|
+
const metadata = {};
|
|
1487
|
+
if (headers["x-total-count"]) {
|
|
1488
|
+
metadata.totalCount = Number(headers["x-total-count"]);
|
|
1489
|
+
}
|
|
1490
|
+
if (headers["x-page"]) {
|
|
1491
|
+
metadata.page = Number(headers["x-page"]);
|
|
1492
|
+
}
|
|
1493
|
+
if (headers["x-per-page"]) {
|
|
1494
|
+
metadata.perPage = Number(headers["x-per-page"]);
|
|
1495
|
+
}
|
|
1496
|
+
if (metadata.totalCount && metadata.perPage) {
|
|
1497
|
+
metadata.totalPages = Math.ceil(metadata.totalCount / metadata.perPage);
|
|
1498
|
+
}
|
|
1499
|
+
if (headers["x-ratelimit-remaining"] || headers["x-rate-limit-remaining"]) {
|
|
1500
|
+
metadata.rateLimit = Number(
|
|
1501
|
+
headers["x-ratelimit-remaining"] || headers["x-rate-limit-remaining"]
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
if (headers["x-ratelimit-reset"] || headers["x-rate-limit-reset"]) {
|
|
1505
|
+
metadata.rateLimitReset = Number(
|
|
1506
|
+
headers["x-ratelimit-reset"] || headers["x-rate-limit-reset"]
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1509
|
+
Object.keys(headers).forEach((key) => {
|
|
1510
|
+
if (key.startsWith("x-") && !metadata[key]) {
|
|
1511
|
+
metadata[key] = headers[key];
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
1514
|
+
return metadata;
|
|
1515
|
+
}
|
|
1516
|
+
getHeader(name) {
|
|
1517
|
+
return this.$raw.headers[name.toLowerCase()];
|
|
1518
|
+
}
|
|
1519
|
+
get hasPagination() {
|
|
1520
|
+
return this.totalCount !== void 0 || this.page !== void 0;
|
|
1521
|
+
}
|
|
1522
|
+
get hasNextPage() {
|
|
1523
|
+
if (!this.page || !this.totalPages) return false;
|
|
1524
|
+
return this.page < this.totalPages;
|
|
1525
|
+
}
|
|
1526
|
+
get hasPrevPage() {
|
|
1527
|
+
if (!this.page) return false;
|
|
1528
|
+
return this.page > 1;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
function parseApiError(error) {
|
|
1532
|
+
const axiosError = error;
|
|
1533
|
+
if (!axiosError.response?.data) {
|
|
1534
|
+
return {
|
|
1535
|
+
message: axiosError.message || "An unexpected error occurred",
|
|
1536
|
+
raw: error
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
const { detail } = axiosError.response.data;
|
|
1540
|
+
if (typeof detail === "string") {
|
|
1541
|
+
return {
|
|
1542
|
+
message: detail,
|
|
1543
|
+
raw: error
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
if (Array.isArray(detail)) {
|
|
1547
|
+
const fields = {};
|
|
1548
|
+
const messages = [];
|
|
1549
|
+
for (const err of detail) {
|
|
1550
|
+
const fieldPath = err.loc.slice(1).join(".");
|
|
1551
|
+
const message = err.msg;
|
|
1552
|
+
if (fieldPath) {
|
|
1553
|
+
if (!fields[fieldPath]) {
|
|
1554
|
+
fields[fieldPath] = [];
|
|
1555
|
+
}
|
|
1556
|
+
fields[fieldPath].push(message);
|
|
1557
|
+
}
|
|
1558
|
+
messages.push(fieldPath ? `${fieldPath}: ${message}` : message);
|
|
1559
|
+
}
|
|
1560
|
+
return {
|
|
1561
|
+
message: messages.join("; "),
|
|
1562
|
+
fields,
|
|
1563
|
+
raw: error
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
return {
|
|
1567
|
+
message: "An error occurred",
|
|
1568
|
+
raw: error
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
function formatFieldErrors(fields) {
|
|
1572
|
+
return Object.entries(fields).map(([field, errors]) => `${field}: ${errors.join(", ")}`).join("\n");
|
|
1573
|
+
}
|
|
1574
|
+
function useApiRequest(requestFn, options = {}) {
|
|
1575
|
+
const loading = vue.ref(false);
|
|
1576
|
+
const error = vue.ref(null);
|
|
1577
|
+
const data = vue.ref(null);
|
|
1578
|
+
const execute = async () => {
|
|
1579
|
+
loading.value = true;
|
|
1580
|
+
error.value = null;
|
|
1581
|
+
try {
|
|
1582
|
+
const result = await requestFn();
|
|
1583
|
+
data.value = result;
|
|
1584
|
+
return result;
|
|
1585
|
+
} catch (e) {
|
|
1586
|
+
error.value = parseApiError(e);
|
|
1587
|
+
return null;
|
|
1588
|
+
} finally {
|
|
1589
|
+
loading.value = false;
|
|
1590
|
+
}
|
|
1591
|
+
};
|
|
1592
|
+
const reset = () => {
|
|
1593
|
+
loading.value = false;
|
|
1594
|
+
error.value = null;
|
|
1595
|
+
data.value = null;
|
|
1596
|
+
};
|
|
1597
|
+
if (options.immediate) {
|
|
1598
|
+
execute();
|
|
1599
|
+
}
|
|
1600
|
+
return { loading, error, data, execute, reset };
|
|
1601
|
+
}
|
|
1602
|
+
function useCancellableRequest(requestFn) {
|
|
1603
|
+
const controller = vue.ref(null);
|
|
1604
|
+
const loading = vue.ref(false);
|
|
1605
|
+
const error = vue.ref(null);
|
|
1606
|
+
const data = vue.ref(null);
|
|
1607
|
+
const execute = async () => {
|
|
1608
|
+
controller.value?.abort();
|
|
1609
|
+
controller.value = new AbortController();
|
|
1610
|
+
loading.value = true;
|
|
1611
|
+
error.value = null;
|
|
1612
|
+
try {
|
|
1613
|
+
const result = await requestFn(controller.value.signal);
|
|
1614
|
+
data.value = result;
|
|
1615
|
+
return result;
|
|
1616
|
+
} catch (e) {
|
|
1617
|
+
if (e.name !== "AbortError" && e.name !== "CanceledError") {
|
|
1618
|
+
error.value = parseApiError(e);
|
|
1619
|
+
}
|
|
1620
|
+
return null;
|
|
1621
|
+
} finally {
|
|
1622
|
+
loading.value = false;
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
const cancel = () => {
|
|
1626
|
+
controller.value?.abort();
|
|
1627
|
+
loading.value = false;
|
|
1628
|
+
};
|
|
1629
|
+
const reset = () => {
|
|
1630
|
+
cancel();
|
|
1631
|
+
error.value = null;
|
|
1632
|
+
data.value = null;
|
|
1633
|
+
};
|
|
1634
|
+
return { loading, error, data, execute, cancel, reset };
|
|
1635
|
+
}
|
|
1636
|
+
function wrapApiForDirectReturn(apiObj) {
|
|
1637
|
+
if (typeof apiObj === "function") {
|
|
1638
|
+
return async (...args) => {
|
|
1639
|
+
const result = await apiObj(...args);
|
|
1640
|
+
if (result && typeof result === "object" && "data" in result && "status" in result) {
|
|
1641
|
+
return ApiResponse.create(result.data, result);
|
|
1642
|
+
}
|
|
1643
|
+
return result;
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
if (typeof apiObj === "object" && apiObj !== null) {
|
|
1647
|
+
const wrapped = {};
|
|
1648
|
+
for (const key in apiObj) {
|
|
1649
|
+
if (key === "stream") {
|
|
1650
|
+
wrapped[key] = apiObj[key];
|
|
1651
|
+
} else {
|
|
1652
|
+
wrapped[key] = wrapApiForDirectReturn(apiObj[key]);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
return wrapped;
|
|
1656
|
+
}
|
|
1657
|
+
return apiObj;
|
|
1658
|
+
}
|
|
1252
1659
|
const axios = axios__default.create({
|
|
1253
1660
|
withCredentials: true
|
|
1254
1661
|
});
|
|
@@ -1496,6 +1903,7 @@ class Bagel {
|
|
|
1496
1903
|
}
|
|
1497
1904
|
}
|
|
1498
1905
|
|
|
1906
|
+
exports.ApiResponse = ApiResponse;
|
|
1499
1907
|
exports.Bagel = Bagel;
|
|
1500
1908
|
exports.StreamController = StreamController;
|
|
1501
1909
|
exports.createSSEStream = createSSEStream;
|
|
@@ -1504,8 +1912,13 @@ exports.dereference = dereference;
|
|
|
1504
1912
|
exports.extractSSEEventInfo = extractSSEEventInfo;
|
|
1505
1913
|
exports.extractSSEEventTypes = extractSSEEventTypes;
|
|
1506
1914
|
exports.formatAPIErrorMessage = formatAPIErrorMessage;
|
|
1915
|
+
exports.formatFieldErrors = formatFieldErrors;
|
|
1507
1916
|
exports.getPath = getPath;
|
|
1508
1917
|
exports.isReferenceObject = isReferenceObject;
|
|
1509
1918
|
exports.isSSEStream = isSSEStream;
|
|
1510
1919
|
exports.isSchemaObject = isSchemaObject;
|
|
1511
1920
|
exports.openAPI = index;
|
|
1921
|
+
exports.parseApiError = parseApiError;
|
|
1922
|
+
exports.useApiRequest = useApiRequest;
|
|
1923
|
+
exports.useCancellableRequest = useCancellableRequest;
|
|
1924
|
+
exports.wrapApiForDirectReturn = wrapApiForDirectReturn;
|