@icib.dev/api-client 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/scripts/generate.js +93 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ setAuthToken(process.env.API_TOKEN);
|
|
|
25
25
|
const res = await apiClient.allegati.list({ page: 1, size: 10 });
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
3. Add verify to your build (fails if docs changed or client was modified):
|
|
28
|
+
3. Add verify to your build (ensures version alignment for production; fails if docs changed or client was modified):
|
|
29
29
|
|
|
30
30
|
```json
|
|
31
31
|
{
|
|
@@ -90,7 +90,9 @@ api/
|
|
|
90
90
|
|
|
91
91
|
### Hash verification
|
|
92
92
|
|
|
93
|
-
Add `api-client-verify` before your build to ensure the generated client matches the current OpenAPI docs.
|
|
93
|
+
Add `api-client-verify` before your build to ensure the generated client matches the current OpenAPI docs. You can insert it in the build step of your utilization library (the app or library that consumes the API client) to verify version alignment before production builds—if the API docs changed or the client was modified, the build fails and you must regenerate.
|
|
94
|
+
|
|
95
|
+
When you run your build, it:
|
|
94
96
|
|
|
95
97
|
1. Reads the manifest (created by `generate`)
|
|
96
98
|
2. Fetches the current docs and compares their hash
|
package/dist/scripts/generate.js
CHANGED
|
@@ -92,6 +92,46 @@ function getDefinitions(doc) {
|
|
|
92
92
|
function getPaths(doc) {
|
|
93
93
|
return doc.paths ?? {};
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Extract response schema. Compatible with:
|
|
97
|
+
* - OpenAPI 2.0: response.schema
|
|
98
|
+
* - OpenAPI 3.0: response.content['application/json'].schema
|
|
99
|
+
*/
|
|
100
|
+
function getResponseSchema(response) {
|
|
101
|
+
if (!response || typeof response !== "object")
|
|
102
|
+
return undefined;
|
|
103
|
+
const r = response;
|
|
104
|
+
// OpenAPI 2.0: schema directly on response
|
|
105
|
+
if (r.schema && typeof r.schema === "object") {
|
|
106
|
+
return r.schema;
|
|
107
|
+
}
|
|
108
|
+
// OpenAPI 3.0: schema under content['application/json'] or content['*/*']
|
|
109
|
+
const content = r.content;
|
|
110
|
+
if (content) {
|
|
111
|
+
const jsonContent = content["application/json"] ??
|
|
112
|
+
content["*/*"] ??
|
|
113
|
+
Object.values(content)[0];
|
|
114
|
+
return jsonContent?.schema;
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Extract request body schema from OpenAPI 3.0 requestBody.content.
|
|
120
|
+
* OpenAPI 2.0 uses parameters with in: "body" (handled separately in extractOperations).
|
|
121
|
+
*/
|
|
122
|
+
function getRequestBodySchema(requestBody) {
|
|
123
|
+
if (!requestBody || typeof requestBody !== "object")
|
|
124
|
+
return undefined;
|
|
125
|
+
const rb = requestBody;
|
|
126
|
+
const content = rb.content;
|
|
127
|
+
if (content) {
|
|
128
|
+
const jsonContent = content["application/json"] ??
|
|
129
|
+
content["*/*"] ??
|
|
130
|
+
Object.values(content)[0];
|
|
131
|
+
return jsonContent?.schema;
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
95
135
|
function schemaToTsType(schema, definitions, refsSeen = new Set()) {
|
|
96
136
|
if (!schema)
|
|
97
137
|
return "unknown";
|
|
@@ -250,8 +290,31 @@ function extractOperations(paths, definitions) {
|
|
|
250
290
|
};
|
|
251
291
|
}
|
|
252
292
|
}
|
|
293
|
+
// OpenAPI 3.0: body in requestBody (OAS 2.0 uses parameters in: "body" above)
|
|
294
|
+
if (!bodyParam && op.requestBody) {
|
|
295
|
+
const bodySchema = getRequestBodySchema(op.requestBody);
|
|
296
|
+
if (bodySchema) {
|
|
297
|
+
const propertyDescriptions = {};
|
|
298
|
+
if (bodySchema.properties) {
|
|
299
|
+
for (const [propName, propSchema] of Object.entries(bodySchema.properties)) {
|
|
300
|
+
const desc = propSchema
|
|
301
|
+
.description ??
|
|
302
|
+
propSchema.title;
|
|
303
|
+
if (desc)
|
|
304
|
+
propertyDescriptions[propName] = desc;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
bodyParam = {
|
|
308
|
+
name: "data",
|
|
309
|
+
schema: bodySchema,
|
|
310
|
+
propertyDescriptions: Object.keys(propertyDescriptions).length > 0
|
|
311
|
+
? propertyDescriptions
|
|
312
|
+
: undefined,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
253
316
|
const successResponse = op.responses?.["200"] ?? op.responses?.["201"];
|
|
254
|
-
const respSchema = successResponse
|
|
317
|
+
const respSchema = getResponseSchema(successResponse);
|
|
255
318
|
const respDesc = successResponse?.description ?? "";
|
|
256
319
|
const xResponseType = successResponse?.["x-response-type"];
|
|
257
320
|
let responseType = "unknown";
|
|
@@ -536,12 +599,23 @@ function generateContextFile(tag, operations, definitions, tagDescription) {
|
|
|
536
599
|
}
|
|
537
600
|
function generateClient(baseUrl) {
|
|
538
601
|
return `// Auto-generated Axios client
|
|
539
|
-
import axios, { type AxiosInstance } from "axios";
|
|
602
|
+
import axios, { type AxiosInstance, AxiosError } from "axios";
|
|
540
603
|
|
|
541
604
|
let _token: string | null = null;
|
|
542
605
|
|
|
543
|
-
export function setAuthToken(token: string | null): void {
|
|
606
|
+
export function setAuthToken(token: string | null, callback?: (token: string | null) => void): void {
|
|
544
607
|
_token = token;
|
|
608
|
+
if (callback) callback(token);
|
|
609
|
+
// insert your own logic here, e.g. save to localStorage, sessionStorage, etc.
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export function getAuthToken(): string | null {
|
|
613
|
+
return _token;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
export function clearAuthToken(): void {
|
|
617
|
+
_token = null;
|
|
618
|
+
// insert your own logic here
|
|
545
619
|
}
|
|
546
620
|
|
|
547
621
|
export const client: AxiosInstance = axios.create({
|
|
@@ -558,6 +632,21 @@ client.interceptors.request.use((config) => {
|
|
|
558
632
|
return config;
|
|
559
633
|
});
|
|
560
634
|
|
|
635
|
+
client.interceptors.response.use(
|
|
636
|
+
(response) => {
|
|
637
|
+
if (response.status === 401) {
|
|
638
|
+
clearAuthToken();
|
|
639
|
+
}
|
|
640
|
+
return response.data;
|
|
641
|
+
},
|
|
642
|
+
(error) => {
|
|
643
|
+
if (error.response.status === 401) {
|
|
644
|
+
clearAuthToken();
|
|
645
|
+
}
|
|
646
|
+
return AxiosError.from(error);
|
|
647
|
+
},
|
|
648
|
+
);
|
|
649
|
+
|
|
561
650
|
/** Options for blob/download endpoints */
|
|
562
651
|
export interface BlobDownloadOptions {
|
|
563
652
|
/** When true, triggers a file download in the browser */
|
|
@@ -631,7 +720,7 @@ function generateIndex(contextTags) {
|
|
|
631
720
|
return exports.join("\n");
|
|
632
721
|
}
|
|
633
722
|
async function main() {
|
|
634
|
-
const { url, out, basePath: basePathOverride, baseUrl: baseUrlOverride } = parseArgs();
|
|
723
|
+
const { url, out, basePath: basePathOverride, baseUrl: baseUrlOverride, } = parseArgs();
|
|
635
724
|
console.log(`Fetching spec from ${url}...`);
|
|
636
725
|
const rawSpec = await loadRawSpec(url);
|
|
637
726
|
const doc = await parseSpec(rawSpec);
|