mulink 1.1.7 → 1.1.9
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 +2 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/lib/{chunk-SL4RMR26.js → chunk-2WVV6AOO.js} +455 -263
- package/dist/lib/chunk-2WVV6AOO.js.map +1 -0
- package/dist/lib/{chunk-RGFU7SMN.cjs → chunk-NC747MZJ.cjs} +455 -263
- package/dist/lib/chunk-NC747MZJ.cjs.map +1 -0
- package/dist/lib/cli.cjs +57 -29
- package/dist/lib/cli.cjs.map +1 -1
- package/dist/lib/cli.js +42 -14
- package/dist/lib/cli.js.map +1 -1
- package/dist/lib/client.cjs +20 -20
- package/dist/lib/client.cjs.map +1 -1
- package/dist/lib/client.d.cts +4 -4
- package/dist/lib/client.d.ts +4 -4
- package/dist/lib/client.js +4 -4
- package/dist/lib/client.js.map +1 -1
- package/dist/lib/index.cjs +177 -15
- package/dist/lib/index.cjs.map +1 -1
- package/dist/lib/index.d.cts +211 -3
- package/dist/lib/index.d.ts +211 -3
- package/dist/lib/index.js +153 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/{version-checker-uF6o5ziX.d.cts → version-checker-DU88tpi8.d.cts} +152 -12
- package/dist/lib/{version-checker-uF6o5ziX.d.ts → version-checker-DU88tpi8.d.ts} +152 -12
- package/package.json +2 -2
- package/dist/lib/chunk-RGFU7SMN.cjs.map +0 -1
- package/dist/lib/chunk-SL4RMR26.js.map +0 -1
|
@@ -7,17 +7,48 @@ var __defProp = Object.defineProperty;
|
|
|
7
7
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
8
|
|
|
9
9
|
// src/core/types.ts
|
|
10
|
-
var BridgeError = class extends Error {
|
|
10
|
+
var BridgeError = class _BridgeError extends Error {
|
|
11
11
|
static {
|
|
12
12
|
__name(this, "BridgeError");
|
|
13
13
|
}
|
|
14
14
|
code;
|
|
15
15
|
details;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
timestamp;
|
|
17
|
+
cause;
|
|
18
|
+
constructor(message, code, details, cause) {
|
|
19
|
+
super(message, { cause });
|
|
18
20
|
this.name = "BridgeError";
|
|
19
21
|
this.code = code;
|
|
20
22
|
this.details = details;
|
|
23
|
+
this.timestamp = Date.now();
|
|
24
|
+
this.cause = cause;
|
|
25
|
+
if (Error.captureStackTrace) {
|
|
26
|
+
Error.captureStackTrace(this, _BridgeError);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Converts the error to a JSON-serializable object
|
|
31
|
+
*
|
|
32
|
+
* @returns JSON representation of the error
|
|
33
|
+
*/
|
|
34
|
+
toJSON() {
|
|
35
|
+
return {
|
|
36
|
+
name: this.name,
|
|
37
|
+
message: this.message,
|
|
38
|
+
code: this.code,
|
|
39
|
+
details: this.details,
|
|
40
|
+
timestamp: this.timestamp,
|
|
41
|
+
stack: this.stack,
|
|
42
|
+
cause: this.cause?.message
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Checks if this error is retryable
|
|
47
|
+
*
|
|
48
|
+
* @returns True if the operation that caused this error can be retried
|
|
49
|
+
*/
|
|
50
|
+
isRetryable() {
|
|
51
|
+
return false;
|
|
21
52
|
}
|
|
22
53
|
};
|
|
23
54
|
var ValidationError = class extends BridgeError {
|
|
@@ -25,28 +56,42 @@ var ValidationError = class extends BridgeError {
|
|
|
25
56
|
__name(this, "ValidationError");
|
|
26
57
|
}
|
|
27
58
|
errors;
|
|
28
|
-
constructor(message, errors) {
|
|
29
|
-
super(message, "VALIDATION_ERROR", errors);
|
|
59
|
+
constructor(message, errors, cause) {
|
|
60
|
+
super(message, "VALIDATION_ERROR", errors, cause);
|
|
30
61
|
this.errors = errors;
|
|
31
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Gets formatted validation error messages
|
|
65
|
+
*
|
|
66
|
+
* @returns Array of formatted error messages
|
|
67
|
+
*/
|
|
68
|
+
getFormattedErrors() {
|
|
69
|
+
return this.errors.errors.map((err) => {
|
|
70
|
+
const path3 = err.path.join(".");
|
|
71
|
+
return `${path3}: ${err.message}`;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
32
74
|
};
|
|
33
75
|
var SchemaParseError = class extends BridgeError {
|
|
34
76
|
static {
|
|
35
77
|
__name(this, "SchemaParseError");
|
|
36
78
|
}
|
|
37
79
|
schemaUrl;
|
|
38
|
-
constructor(message, schemaUrl) {
|
|
39
|
-
super(message, "SCHEMA_PARSE_ERROR", { schemaUrl });
|
|
80
|
+
constructor(message, schemaUrl, cause) {
|
|
81
|
+
super(message, "SCHEMA_PARSE_ERROR", { schemaUrl }, cause);
|
|
40
82
|
this.schemaUrl = schemaUrl;
|
|
41
83
|
}
|
|
84
|
+
isRetryable() {
|
|
85
|
+
return this.cause instanceof TypeError && this.cause.message.includes("fetch");
|
|
86
|
+
}
|
|
42
87
|
};
|
|
43
88
|
var GenerationError = class extends BridgeError {
|
|
44
89
|
static {
|
|
45
90
|
__name(this, "GenerationError");
|
|
46
91
|
}
|
|
47
92
|
file;
|
|
48
|
-
constructor(message, file) {
|
|
49
|
-
super(message, "GENERATION_ERROR", { file });
|
|
93
|
+
constructor(message, file, cause) {
|
|
94
|
+
super(message, "GENERATION_ERROR", { file }, cause);
|
|
50
95
|
this.file = file;
|
|
51
96
|
}
|
|
52
97
|
};
|
|
@@ -134,8 +179,8 @@ var BridgeLogger = class {
|
|
|
134
179
|
if (!this.options.colors || !process.stdout.isTTY) {
|
|
135
180
|
console.log(message);
|
|
136
181
|
return {
|
|
137
|
-
succeed: /* @__PURE__ */ __name((msg) => console.log(colorize(`\u2713 ${msg
|
|
138
|
-
fail: /* @__PURE__ */ __name((msg) => console.error(colorize(`\u2717 ${msg
|
|
182
|
+
succeed: /* @__PURE__ */ __name((msg) => console.log(colorize(`\u2713 ${msg ?? message}`, "green")), "succeed"),
|
|
183
|
+
fail: /* @__PURE__ */ __name((msg) => console.error(colorize(`\u2717 ${msg ?? message}`, "red")), "fail"),
|
|
139
184
|
stop: /* @__PURE__ */ __name(() => {
|
|
140
185
|
}, "stop")
|
|
141
186
|
};
|
|
@@ -143,17 +188,20 @@ var BridgeLogger = class {
|
|
|
143
188
|
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
144
189
|
let i = 0;
|
|
145
190
|
const interval = setInterval(() => {
|
|
146
|
-
|
|
191
|
+
const frame = frames[i++ % frames.length];
|
|
192
|
+
if (frame) {
|
|
193
|
+
process.stdout.write(`\r${colorize(frame, "cyan")} ${message}`);
|
|
194
|
+
}
|
|
147
195
|
}, 100);
|
|
148
196
|
return {
|
|
149
197
|
succeed: /* @__PURE__ */ __name((msg) => {
|
|
150
198
|
clearInterval(interval);
|
|
151
|
-
process.stdout.write(`\r${colorize("\u2713", "green")} ${msg
|
|
199
|
+
process.stdout.write(`\r${colorize("\u2713", "green")} ${msg ?? message}
|
|
152
200
|
`);
|
|
153
201
|
}, "succeed"),
|
|
154
202
|
fail: /* @__PURE__ */ __name((msg) => {
|
|
155
203
|
clearInterval(interval);
|
|
156
|
-
process.stdout.write(`\r${colorize("\u2717", "red")} ${msg
|
|
204
|
+
process.stdout.write(`\r${colorize("\u2717", "red")} ${msg ?? message}
|
|
157
205
|
`);
|
|
158
206
|
}, "fail"),
|
|
159
207
|
stop: /* @__PURE__ */ __name(() => {
|
|
@@ -176,7 +224,7 @@ var VersionChecker = class {
|
|
|
176
224
|
constructor(packageName, currentVersion, logger) {
|
|
177
225
|
this.packageName = packageName;
|
|
178
226
|
this.currentVersion = currentVersion;
|
|
179
|
-
this.logger = logger
|
|
227
|
+
this.logger = logger ?? new BridgeLogger({ prefix: "\u{1F50D} Version Check" });
|
|
180
228
|
}
|
|
181
229
|
/**
|
|
182
230
|
* Check if a new version is available
|
|
@@ -231,7 +279,7 @@ var VersionChecker = class {
|
|
|
231
279
|
/**
|
|
232
280
|
* Get the appropriate upgrade command for the detected package manager
|
|
233
281
|
*/
|
|
234
|
-
getUpgradeCommand(
|
|
282
|
+
getUpgradeCommand(_versionInfo) {
|
|
235
283
|
const packageManager = this.detectPackageManager();
|
|
236
284
|
switch (packageManager) {
|
|
237
285
|
case "yarn":
|
|
@@ -305,8 +353,8 @@ var VersionChecker = class {
|
|
|
305
353
|
const currentParts = this.parseVersion(current);
|
|
306
354
|
const latestParts = this.parseVersion(latest);
|
|
307
355
|
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
|
|
308
|
-
const currentPart = currentParts[i]
|
|
309
|
-
const latestPart = latestParts[i]
|
|
356
|
+
const currentPart = currentParts[i] ?? 0;
|
|
357
|
+
const latestPart = latestParts[i] ?? 0;
|
|
310
358
|
if (latestPart > currentPart) {
|
|
311
359
|
return true;
|
|
312
360
|
} else if (latestPart < currentPart) {
|
|
@@ -380,7 +428,7 @@ function createBridgeVersionChecker(logger) {
|
|
|
380
428
|
if (existsSync(pkgPath)) {
|
|
381
429
|
const pkgRaw = readFileSync(pkgPath, { encoding: "utf-8" });
|
|
382
430
|
const pkg = JSON.parse(pkgRaw);
|
|
383
|
-
if (pkg
|
|
431
|
+
if (pkg.version) {
|
|
384
432
|
currentVersion = pkg.version;
|
|
385
433
|
}
|
|
386
434
|
} else {
|
|
@@ -388,13 +436,13 @@ function createBridgeVersionChecker(logger) {
|
|
|
388
436
|
if (existsSync(cwdPkg)) {
|
|
389
437
|
const pkgRaw = readFileSync(cwdPkg, { encoding: "utf-8" });
|
|
390
438
|
const pkg = JSON.parse(pkgRaw);
|
|
391
|
-
if (pkg
|
|
439
|
+
if (pkg.version) {
|
|
392
440
|
currentVersion = pkg.version;
|
|
393
441
|
}
|
|
394
442
|
}
|
|
395
443
|
}
|
|
396
444
|
} catch (err) {
|
|
397
|
-
const l = logger
|
|
445
|
+
const l = logger ?? new BridgeLogger({ prefix: "\u{1F50D} Version Check" });
|
|
398
446
|
l.debug("Failed to read package.json for current version, falling back to default", err);
|
|
399
447
|
}
|
|
400
448
|
return new VersionChecker(packageName, currentVersion, logger);
|
|
@@ -432,7 +480,7 @@ var OpenApiSchemaParser = class {
|
|
|
432
480
|
async fetchSchema(schemaUrl) {
|
|
433
481
|
const versionChecker = createBridgeVersionChecker();
|
|
434
482
|
const currentVersion = versionChecker.getCurrentVersion();
|
|
435
|
-
if (!schemaUrl
|
|
483
|
+
if (!schemaUrl.trim()) {
|
|
436
484
|
throw new SchemaParseError("Schema URL is required and cannot be empty", schemaUrl);
|
|
437
485
|
}
|
|
438
486
|
if (schemaUrl.startsWith("http")) {
|
|
@@ -453,7 +501,7 @@ var OpenApiSchemaParser = class {
|
|
|
453
501
|
schemaUrl
|
|
454
502
|
);
|
|
455
503
|
}
|
|
456
|
-
const contentType = response.headers.get("content-type")
|
|
504
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
457
505
|
if (!contentType.includes("application/json") && !contentType.includes("application/yaml") && !contentType.includes("text/yaml")) {
|
|
458
506
|
throw new SchemaParseError(
|
|
459
507
|
`Unsupported content type: ${contentType}. Expected JSON or YAML.`,
|
|
@@ -483,10 +531,10 @@ var OpenApiSchemaParser = class {
|
|
|
483
531
|
);
|
|
484
532
|
}
|
|
485
533
|
}
|
|
486
|
-
|
|
534
|
+
parseDocument(document) {
|
|
487
535
|
const version = this.detectVersion(document);
|
|
488
536
|
const normalizedDocument = this.normalizeDocument(document, version);
|
|
489
|
-
return {
|
|
537
|
+
return Promise.resolve({
|
|
490
538
|
version,
|
|
491
539
|
document: normalizedDocument,
|
|
492
540
|
openApiSpec: normalizedDocument,
|
|
@@ -495,7 +543,7 @@ var OpenApiSchemaParser = class {
|
|
|
495
543
|
security: this.extractSecurity(normalizedDocument),
|
|
496
544
|
servers: this.extractServers(normalizedDocument, document),
|
|
497
545
|
metadata: this.extractMetadata(normalizedDocument)
|
|
498
|
-
};
|
|
546
|
+
});
|
|
499
547
|
}
|
|
500
548
|
detectVersion(document) {
|
|
501
549
|
if (!document || typeof document !== "object") {
|
|
@@ -530,7 +578,7 @@ var OpenApiSchemaParser = class {
|
|
|
530
578
|
info: v2Doc.info,
|
|
531
579
|
servers: v2Doc.host ? [
|
|
532
580
|
{
|
|
533
|
-
url: `${v2Doc.schemes?.[0]
|
|
581
|
+
url: `${v2Doc.schemes?.[0] ?? "https"}://${v2Doc.host}${v2Doc.basePath ?? ""}`
|
|
534
582
|
}
|
|
535
583
|
] : [],
|
|
536
584
|
paths: {},
|
|
@@ -675,20 +723,20 @@ var OpenApiSchemaParser = class {
|
|
|
675
723
|
flows: {
|
|
676
724
|
implicit: v2Scheme.flow === "implicit" ? {
|
|
677
725
|
authorizationUrl: v2Scheme.authorizationUrl,
|
|
678
|
-
scopes: v2Scheme.scopes
|
|
726
|
+
scopes: v2Scheme.scopes ?? {}
|
|
679
727
|
} : void 0,
|
|
680
728
|
password: v2Scheme.flow === "password" ? {
|
|
681
729
|
tokenUrl: v2Scheme.tokenUrl,
|
|
682
|
-
scopes: v2Scheme.scopes
|
|
730
|
+
scopes: v2Scheme.scopes ?? {}
|
|
683
731
|
} : void 0,
|
|
684
732
|
clientCredentials: v2Scheme.flow === "application" ? {
|
|
685
733
|
tokenUrl: v2Scheme.tokenUrl,
|
|
686
|
-
scopes: v2Scheme.scopes
|
|
734
|
+
scopes: v2Scheme.scopes ?? {}
|
|
687
735
|
} : void 0,
|
|
688
736
|
authorizationCode: v2Scheme.flow === "accessCode" ? {
|
|
689
737
|
authorizationUrl: v2Scheme.authorizationUrl,
|
|
690
738
|
tokenUrl: v2Scheme.tokenUrl,
|
|
691
|
-
scopes: v2Scheme.scopes
|
|
739
|
+
scopes: v2Scheme.scopes ?? {}
|
|
692
740
|
} : void 0
|
|
693
741
|
}
|
|
694
742
|
};
|
|
@@ -714,18 +762,18 @@ var OpenApiSchemaParser = class {
|
|
|
714
762
|
const operation = pathItem[method.toLowerCase()];
|
|
715
763
|
if (!operation) continue;
|
|
716
764
|
const endpoint = {
|
|
717
|
-
id: operation.operationId
|
|
765
|
+
id: operation.operationId ?? `${method.toLowerCase()}_${path3.replace(/[^a-zA-Z0-9]/g, "_")}`,
|
|
718
766
|
path: path3,
|
|
719
767
|
method,
|
|
720
768
|
operationId: operation.operationId,
|
|
721
769
|
summary: operation.summary,
|
|
722
770
|
description: operation.description,
|
|
723
|
-
tags: operation.tags
|
|
771
|
+
tags: operation.tags ?? [],
|
|
724
772
|
parameters: this.extractParameters(operation.parameters, pathItem.parameters, document),
|
|
725
773
|
requestBody: this.extractRequestBody(operation.requestBody, document),
|
|
726
774
|
responses: this.extractResponses(operation.responses, document),
|
|
727
|
-
security: operation.security
|
|
728
|
-
deprecated: operation.deprecated
|
|
775
|
+
security: operation.security ?? [],
|
|
776
|
+
deprecated: operation.deprecated ?? false,
|
|
729
777
|
metadata: this.extractEndpointMetadata(operation)
|
|
730
778
|
};
|
|
731
779
|
endpoints.push(endpoint);
|
|
@@ -735,7 +783,7 @@ var OpenApiSchemaParser = class {
|
|
|
735
783
|
}
|
|
736
784
|
extractParameters(operationParams, pathParams, document) {
|
|
737
785
|
const parameters = [];
|
|
738
|
-
const allParams = [...pathParams
|
|
786
|
+
const allParams = [...pathParams ?? [], ...operationParams ?? []];
|
|
739
787
|
for (const param of allParams) {
|
|
740
788
|
if (!param) continue;
|
|
741
789
|
if ("$ref" in param) {
|
|
@@ -745,11 +793,11 @@ var OpenApiSchemaParser = class {
|
|
|
745
793
|
const parameter = {
|
|
746
794
|
name: param.name,
|
|
747
795
|
in: param.in,
|
|
748
|
-
required: param.required
|
|
796
|
+
required: param.required ?? param.in === "path",
|
|
749
797
|
schema: this.convertSchemaToZod(param.schema, document),
|
|
750
798
|
description: param.description,
|
|
751
799
|
example: param.example,
|
|
752
|
-
deprecated: param.deprecated
|
|
800
|
+
deprecated: param.deprecated ?? false
|
|
753
801
|
};
|
|
754
802
|
parameters.push(parameter);
|
|
755
803
|
}
|
|
@@ -768,7 +816,7 @@ var OpenApiSchemaParser = class {
|
|
|
768
816
|
}
|
|
769
817
|
}
|
|
770
818
|
return {
|
|
771
|
-
required: requestBody.required
|
|
819
|
+
required: requestBody.required ?? false,
|
|
772
820
|
description: requestBody.description,
|
|
773
821
|
content
|
|
774
822
|
};
|
|
@@ -808,7 +856,7 @@ var OpenApiSchemaParser = class {
|
|
|
808
856
|
schema: this.convertSchemaToZod(schema, document),
|
|
809
857
|
description: schema.description,
|
|
810
858
|
example: schema.example,
|
|
811
|
-
deprecated: schema.deprecated
|
|
859
|
+
deprecated: schema.deprecated ?? false
|
|
812
860
|
});
|
|
813
861
|
}
|
|
814
862
|
}
|
|
@@ -842,7 +890,7 @@ var OpenApiSchemaParser = class {
|
|
|
842
890
|
const v2Doc = originalDoc;
|
|
843
891
|
return [
|
|
844
892
|
{
|
|
845
|
-
url: `${v2Doc.schemes?.[0]
|
|
893
|
+
url: `${v2Doc.schemes?.[0] ?? "https"}://${v2Doc.host}${v2Doc.basePath ?? ""}`,
|
|
846
894
|
description: "Converted from Swagger 2.0"
|
|
847
895
|
}
|
|
848
896
|
];
|
|
@@ -862,9 +910,9 @@ var OpenApiSchemaParser = class {
|
|
|
862
910
|
}
|
|
863
911
|
extractEndpointMetadata(operation) {
|
|
864
912
|
return {
|
|
865
|
-
requiresAuth: operation.security
|
|
913
|
+
requiresAuth: (operation.security?.length ?? 0) > 0,
|
|
866
914
|
cacheStrategy: "default",
|
|
867
|
-
revalidationTags: operation.tags
|
|
915
|
+
revalidationTags: operation.tags ?? [],
|
|
868
916
|
streaming: false,
|
|
869
917
|
fileUpload: this.hasFileUpload(operation)
|
|
870
918
|
};
|
|
@@ -872,7 +920,7 @@ var OpenApiSchemaParser = class {
|
|
|
872
920
|
hasFileUpload(operation) {
|
|
873
921
|
if (!operation.requestBody || "$ref" in operation.requestBody) return false;
|
|
874
922
|
const content = operation.requestBody.content;
|
|
875
|
-
return Object.keys(content
|
|
923
|
+
return Object.keys(content ?? {}).some(
|
|
876
924
|
(mediaType) => mediaType.includes("multipart/form-data") || mediaType.includes("application/octet-stream")
|
|
877
925
|
);
|
|
878
926
|
}
|
|
@@ -960,7 +1008,7 @@ var OpenApiSchemaParser = class {
|
|
|
960
1008
|
case "object":
|
|
961
1009
|
if (schema.properties) {
|
|
962
1010
|
const shape = {};
|
|
963
|
-
const required = schema.required
|
|
1011
|
+
const required = schema.required ?? [];
|
|
964
1012
|
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
965
1013
|
let propZodSchema = this.convertSchemaToZod(propSchema, document);
|
|
966
1014
|
if (!required.includes(propName)) {
|
|
@@ -992,7 +1040,7 @@ var ApiClientGenerator = class {
|
|
|
992
1040
|
* Generate import paths based on configuration
|
|
993
1041
|
*/
|
|
994
1042
|
buildImportPath(relativePath) {
|
|
995
|
-
const outputDirectory = this.configuration.outputDir
|
|
1043
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
996
1044
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
997
1045
|
let importBasePath = outputDirectory;
|
|
998
1046
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -1000,24 +1048,24 @@ var ApiClientGenerator = class {
|
|
|
1000
1048
|
}
|
|
1001
1049
|
return `@/${importBasePath}/${cleanPath}`;
|
|
1002
1050
|
}
|
|
1003
|
-
|
|
1051
|
+
generate(context) {
|
|
1004
1052
|
const { schema } = context;
|
|
1005
1053
|
const generatedFiles = [];
|
|
1006
1054
|
generatedFiles.push(this.generateEnhancedBaseClient());
|
|
1007
1055
|
generatedFiles.push(...this.generateEndpointClients(schema.endpoints));
|
|
1008
1056
|
generatedFiles.push(this.generateMainClient(schema.endpoints));
|
|
1009
1057
|
generatedFiles.push(this.generateClientUtilities());
|
|
1010
|
-
return generatedFiles;
|
|
1058
|
+
return Promise.resolve(generatedFiles);
|
|
1011
1059
|
}
|
|
1012
1060
|
generateEnhancedBaseClient() {
|
|
1013
1061
|
const { api, auth } = this.configuration;
|
|
1014
1062
|
const authUtilsImport = this.buildImportPath("auth/utils");
|
|
1015
|
-
const authPath = auth?.authPath
|
|
1016
|
-
const tokenGetter = auth?.tokenGetter
|
|
1063
|
+
const authPath = auth?.authPath ?? "@/lib/auth";
|
|
1064
|
+
const tokenGetter = auth?.tokenGetter ?? "auth";
|
|
1017
1065
|
const content = `/**
|
|
1018
1066
|
* The HTTP client is automatically created by "mulink"
|
|
1019
1067
|
*
|
|
1020
|
-
* Next.js 16.0.
|
|
1068
|
+
* Next.js 16.0.7 Best Practices:
|
|
1021
1069
|
* - Proper separation of client/server code
|
|
1022
1070
|
* - Dynamic imports for server-only modules
|
|
1023
1071
|
* - Works in both Server Components and Client Components
|
|
@@ -1028,7 +1076,7 @@ import { cache } from 'react'
|
|
|
1028
1076
|
|
|
1029
1077
|
/**
|
|
1030
1078
|
* Server-only modules interface
|
|
1031
|
-
* Next.js 16.0.
|
|
1079
|
+
* Next.js 16.0.7: These modules are only available on the server
|
|
1032
1080
|
* We use dynamic imports to avoid bundling them in the client
|
|
1033
1081
|
*/
|
|
1034
1082
|
type NextHeadersModule = typeof import('next/headers')
|
|
@@ -1052,7 +1100,7 @@ function toMutableHeaders(source: NextReadonlyHeaders) {
|
|
|
1052
1100
|
|
|
1053
1101
|
/**
|
|
1054
1102
|
* Lazy load server-only modules only when needed (server-side)
|
|
1055
|
-
* Next.js 16.0.
|
|
1103
|
+
* Next.js 16.0.7: This ensures server-only code is not bundled in the client
|
|
1056
1104
|
*
|
|
1057
1105
|
* @returns Server-only modules or undefined if on client-side
|
|
1058
1106
|
*/
|
|
@@ -1060,7 +1108,7 @@ async function getServerModules() {
|
|
|
1060
1108
|
if (serverOnlyModules !== null) return serverOnlyModules
|
|
1061
1109
|
|
|
1062
1110
|
// Only attempt to import on server-side
|
|
1063
|
-
// Next.js 16.0.
|
|
1111
|
+
// Next.js 16.0.7: typeof window check ensures we're on the server
|
|
1064
1112
|
if (typeof window === 'undefined') {
|
|
1065
1113
|
try {
|
|
1066
1114
|
const headersModule = await import('next/headers').catch(() => null)
|
|
@@ -1286,11 +1334,11 @@ export class BaseApiClient {
|
|
|
1286
1334
|
private readonly middleware: RequestMiddleware[] = []
|
|
1287
1335
|
|
|
1288
1336
|
constructor() {
|
|
1289
|
-
this.baseUrl = process.env.NEXT_PUBLIC_API_URL || process.env.API_BASE_URL || '${api
|
|
1290
|
-
this.defaultTimeout = ${api
|
|
1291
|
-
this.defaultRetries = ${api
|
|
1292
|
-
this.defaultHeaders = ${JSON.stringify(api
|
|
1293
|
-
this.defaultUserAgent = '${api
|
|
1337
|
+
this.baseUrl = process.env.NEXT_PUBLIC_API_URL || process.env.API_BASE_URL || '${api.baseUrl || "http://localhost:3000/api"}'
|
|
1338
|
+
this.defaultTimeout = ${api.timeout || 3e4}
|
|
1339
|
+
this.defaultRetries = ${api.retries || 3}
|
|
1340
|
+
this.defaultHeaders = ${JSON.stringify(api.headers || {}, null, 6)}
|
|
1341
|
+
this.defaultUserAgent = '${api.userAgent || "Mulink-Client/3.4.5"}'
|
|
1294
1342
|
|
|
1295
1343
|
// Add default middleware
|
|
1296
1344
|
this.addMiddleware({
|
|
@@ -1464,16 +1512,26 @@ export class BaseApiClient {
|
|
|
1464
1512
|
}
|
|
1465
1513
|
|
|
1466
1514
|
// Add query parameters with proper encoding
|
|
1515
|
+
// Filter out undefined, null, empty strings, and empty objects to prevent 422 errors
|
|
1467
1516
|
const searchParams = new URLSearchParams()
|
|
1468
1517
|
for (const [key, value] of Object.entries(queryParameters)) {
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1518
|
+
// Skip undefined, null, and empty strings
|
|
1519
|
+
if (value === undefined || value === null || value === '') {
|
|
1520
|
+
continue
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
// Skip empty objects (like {})
|
|
1524
|
+
if (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) {
|
|
1525
|
+
continue
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
if (Array.isArray(value)) {
|
|
1529
|
+
value.forEach(v => searchParams.append(key, String(v)))
|
|
1530
|
+
} else if (typeof value === 'object') {
|
|
1531
|
+
// Only serialize non-empty objects
|
|
1532
|
+
searchParams.append(key, JSON.stringify(value))
|
|
1533
|
+
} else {
|
|
1534
|
+
searchParams.append(key, String(value))
|
|
1477
1535
|
}
|
|
1478
1536
|
}
|
|
1479
1537
|
|
|
@@ -1646,7 +1704,7 @@ export class BaseApiClient {
|
|
|
1646
1704
|
controller.abort()
|
|
1647
1705
|
}, timeout)
|
|
1648
1706
|
|
|
1649
|
-
// Next.js 16.0.
|
|
1707
|
+
// Next.js 16.0.7: Build fetch options with cache tags and connection
|
|
1650
1708
|
// Only include next options if we're on the server (Next.js App Router)
|
|
1651
1709
|
const fetchInit: RequestInit & { next?: { tags?: string[]; revalidate?: number | false; connection?: string } } = {
|
|
1652
1710
|
...requestConfig,
|
|
@@ -1654,7 +1712,7 @@ export class BaseApiClient {
|
|
|
1654
1712
|
}
|
|
1655
1713
|
|
|
1656
1714
|
// Add Next.js-specific options only if we have cache tags, revalidate, or connection
|
|
1657
|
-
// Next.js 16.0.
|
|
1715
|
+
// Next.js 16.0.7: Enhanced cache tag support with updateTag
|
|
1658
1716
|
if (cacheTags && cacheTags.length > 0 || revalidate !== undefined || connection) {
|
|
1659
1717
|
fetchInit.next = {}
|
|
1660
1718
|
|
|
@@ -1666,7 +1724,7 @@ export class BaseApiClient {
|
|
|
1666
1724
|
fetchInit.next.revalidate = revalidate === false ? false : revalidate
|
|
1667
1725
|
}
|
|
1668
1726
|
|
|
1669
|
-
// Next.js 16.0.
|
|
1727
|
+
// Next.js 16.0.7: Connection keep-alive for persistent connections
|
|
1670
1728
|
if (connection) {
|
|
1671
1729
|
fetchInit.next.connection = connection
|
|
1672
1730
|
}
|
|
@@ -1674,7 +1732,7 @@ export class BaseApiClient {
|
|
|
1674
1732
|
|
|
1675
1733
|
const response = await fetch(url, fetchInit)
|
|
1676
1734
|
|
|
1677
|
-
// Next.js 16.0.
|
|
1735
|
+
// Next.js 16.0.7: Update cache tags dynamically using updateTag from next/cache
|
|
1678
1736
|
// This allows for granular cache invalidation
|
|
1679
1737
|
if (cacheTags && cacheTags.length > 0) {
|
|
1680
1738
|
try {
|
|
@@ -1938,7 +1996,7 @@ async get<TData>(
|
|
|
1938
1996
|
}
|
|
1939
1997
|
generateClientUtilities() {
|
|
1940
1998
|
const { api } = this.configuration;
|
|
1941
|
-
const errorHandling = api
|
|
1999
|
+
const errorHandling = api.errorHandling;
|
|
1942
2000
|
const enableAuthErrorHandling = errorHandling?.enableAuthErrorHandling !== false;
|
|
1943
2001
|
const authErrorHandlerPath = errorHandling?.authErrorHandlerPath || "@/lib/auth-error-handler";
|
|
1944
2002
|
const authErrorMiddlewareCode = enableAuthErrorHandling ? `
|
|
@@ -2335,7 +2393,7 @@ ${clientMethods}
|
|
|
2335
2393
|
const requestOptionsString = requestOptions.join("\n");
|
|
2336
2394
|
const methodSignature = parameterTypes.length > 1 ? `options: {
|
|
2337
2395
|
${parameterTypes.join("\n ")}
|
|
2338
|
-
}` : parameterTypes.length === 1 && parameterTypes[0].includes("config?") ? "options?: { config?: RequestConfiguration }" : `options: { ${parameterTypes.join(", ")} }`;
|
|
2396
|
+
}` : parameterTypes.length === 1 && parameterTypes[0] && parameterTypes[0].includes("config?") ? "options?: { config?: RequestConfiguration }" : `options: { ${parameterTypes.join(", ")} }`;
|
|
2339
2397
|
if (isQuery) {
|
|
2340
2398
|
return `${documentation}
|
|
2341
2399
|
${methodName} = cache(async (${methodSignature}) => {
|
|
@@ -2606,7 +2664,7 @@ var SchemaGenerator = class {
|
|
|
2606
2664
|
visiting.add(schemaName);
|
|
2607
2665
|
const schema = schemaMap.get(schemaName);
|
|
2608
2666
|
if (schema) {
|
|
2609
|
-
const dependencies = dependencyGraph.get(schemaName)
|
|
2667
|
+
const dependencies = dependencyGraph.get(schemaName) ?? /* @__PURE__ */ new Set();
|
|
2610
2668
|
for (const dep of dependencies) {
|
|
2611
2669
|
if (schemaMap.has(dep)) {
|
|
2612
2670
|
visit(dep);
|
|
@@ -2639,13 +2697,13 @@ var SchemaGenerator = class {
|
|
|
2639
2697
|
findSchemaDependencies(schema) {
|
|
2640
2698
|
const dependencies = [];
|
|
2641
2699
|
const schemaObj = schema.schema;
|
|
2642
|
-
if (schemaObj
|
|
2700
|
+
if (schemaObj?._def) {
|
|
2643
2701
|
this.extractDependenciesFromZodSchema(schemaObj, dependencies);
|
|
2644
2702
|
}
|
|
2645
2703
|
return dependencies;
|
|
2646
2704
|
}
|
|
2647
2705
|
extractDependenciesFromZodSchema(zodSchema, dependencies, visited = /* @__PURE__ */ new Set()) {
|
|
2648
|
-
if (!zodSchema
|
|
2706
|
+
if (!zodSchema?._def) return;
|
|
2649
2707
|
const def = zodSchema._def;
|
|
2650
2708
|
if (def._schemaRef) {
|
|
2651
2709
|
const refName = def._schemaRef;
|
|
@@ -2668,7 +2726,7 @@ var SchemaGenerator = class {
|
|
|
2668
2726
|
case "ZodObject":
|
|
2669
2727
|
if (def.shape) {
|
|
2670
2728
|
const shape = def.shape();
|
|
2671
|
-
for (const [
|
|
2729
|
+
for (const [_key, value] of Object.entries(shape)) {
|
|
2672
2730
|
this.extractDependenciesFromZodSchema(value, dependencies, visited);
|
|
2673
2731
|
}
|
|
2674
2732
|
}
|
|
@@ -2700,7 +2758,7 @@ var SchemaGenerator = class {
|
|
|
2700
2758
|
break;
|
|
2701
2759
|
}
|
|
2702
2760
|
}
|
|
2703
|
-
|
|
2761
|
+
generateSchemasFile(context) {
|
|
2704
2762
|
const { schemas, endpoints } = context.schema;
|
|
2705
2763
|
const imports = ['import { z } from "zod"'];
|
|
2706
2764
|
const schemaExports = [];
|
|
@@ -2734,7 +2792,7 @@ var SchemaGenerator = class {
|
|
|
2734
2792
|
|
|
2735
2793
|
${content}`;
|
|
2736
2794
|
}
|
|
2737
|
-
return {
|
|
2795
|
+
return Promise.resolve({
|
|
2738
2796
|
path: "schemas/index.ts",
|
|
2739
2797
|
content,
|
|
2740
2798
|
type: "typescript",
|
|
@@ -2743,7 +2801,7 @@ ${content}`;
|
|
|
2743
2801
|
imports: ["zod"],
|
|
2744
2802
|
dependencies: []
|
|
2745
2803
|
}
|
|
2746
|
-
};
|
|
2804
|
+
});
|
|
2747
2805
|
}
|
|
2748
2806
|
generateEndpointSchemas(endpoint) {
|
|
2749
2807
|
const definitions = [];
|
|
@@ -2778,6 +2836,9 @@ ${content}`;
|
|
|
2778
2836
|
}
|
|
2779
2837
|
const exportName = `${operationName}RequestSchema`;
|
|
2780
2838
|
const primaryContent = endpoint.requestBody.content[0];
|
|
2839
|
+
if (!primaryContent) {
|
|
2840
|
+
throw new Error(`No content found for request body in ${endpoint.method} ${endpoint.path}`);
|
|
2841
|
+
}
|
|
2781
2842
|
const zodSchemaString = this.zodSchemaToString(primaryContent.schema);
|
|
2782
2843
|
const definition = [
|
|
2783
2844
|
`/**`,
|
|
@@ -2801,17 +2862,22 @@ ${content}`;
|
|
|
2801
2862
|
);
|
|
2802
2863
|
if (successResponses.length > 0) {
|
|
2803
2864
|
const successResponse = successResponses[0];
|
|
2865
|
+
if (!successResponse) {
|
|
2866
|
+
return { definitions, exports };
|
|
2867
|
+
}
|
|
2804
2868
|
let zodSchemaString = "z.void()";
|
|
2805
2869
|
if (successResponse.content && successResponse.content.length > 0) {
|
|
2806
2870
|
const primaryContent = successResponse.content[0];
|
|
2807
|
-
|
|
2871
|
+
if (primaryContent) {
|
|
2872
|
+
zodSchemaString = this.zodSchemaToString(primaryContent.schema);
|
|
2873
|
+
}
|
|
2808
2874
|
}
|
|
2809
2875
|
const exportName = `${operationName}ResponseSchema`;
|
|
2810
2876
|
const definition = [
|
|
2811
2877
|
`/**`,
|
|
2812
2878
|
` * Success response schema for ${endpoint.method} ${endpoint.path}`,
|
|
2813
2879
|
` * Status: ${successResponse.statusCode}`,
|
|
2814
|
-
` * ${successResponse.description}`,
|
|
2880
|
+
` * ${successResponse.description ?? ""}`,
|
|
2815
2881
|
` */`,
|
|
2816
2882
|
`export const ${exportName} = ${zodSchemaString}`,
|
|
2817
2883
|
"",
|
|
@@ -2822,15 +2888,18 @@ ${content}`;
|
|
|
2822
2888
|
}
|
|
2823
2889
|
if (errorResponses.length > 0) {
|
|
2824
2890
|
const errorResponse = errorResponses[0];
|
|
2825
|
-
if (errorResponse
|
|
2891
|
+
if (errorResponse?.content && errorResponse.content.length > 0) {
|
|
2826
2892
|
const exportName = `${operationName}ErrorSchema`;
|
|
2827
2893
|
const primaryContent = errorResponse.content[0];
|
|
2894
|
+
if (!primaryContent) {
|
|
2895
|
+
return { definitions, exports };
|
|
2896
|
+
}
|
|
2828
2897
|
const zodSchemaString = this.zodSchemaToString(primaryContent.schema);
|
|
2829
2898
|
const definition = [
|
|
2830
2899
|
`/**`,
|
|
2831
2900
|
` * Error response schema for ${endpoint.method} ${endpoint.path}`,
|
|
2832
2901
|
` * Status: ${errorResponse.statusCode}`,
|
|
2833
|
-
` * ${errorResponse.description}`,
|
|
2902
|
+
` * ${errorResponse.description ?? ""}`,
|
|
2834
2903
|
` */`,
|
|
2835
2904
|
`export const ${exportName} = ${zodSchemaString}`,
|
|
2836
2905
|
"",
|
|
@@ -2921,8 +2990,8 @@ ${schemaProperties.join(",\n")}
|
|
|
2921
2990
|
};
|
|
2922
2991
|
}
|
|
2923
2992
|
zodSchemaToString(schema, context) {
|
|
2924
|
-
if (!schema
|
|
2925
|
-
if (schema
|
|
2993
|
+
if (!schema?._def) {
|
|
2994
|
+
if (schema?._schemaRef) {
|
|
2926
2995
|
const refName = schema._schemaRef;
|
|
2927
2996
|
const registeredSchema = this.schemaRegistry.get(refName);
|
|
2928
2997
|
if (registeredSchema) {
|
|
@@ -3090,15 +3159,21 @@ export const errorMessages = {
|
|
|
3090
3159
|
const assignments = line.match(/(\w+)\s*[:=]/g);
|
|
3091
3160
|
if (assignments) {
|
|
3092
3161
|
assignments.forEach((match) => {
|
|
3093
|
-
const
|
|
3094
|
-
|
|
3162
|
+
const parts = match.split(/[:=]/);
|
|
3163
|
+
if (parts[0]) {
|
|
3164
|
+
const identifier = parts[0].trim();
|
|
3165
|
+
usedIdentifiers.add(identifier);
|
|
3166
|
+
}
|
|
3095
3167
|
});
|
|
3096
3168
|
}
|
|
3097
3169
|
const functionCalls = line.match(/(\w+)\s*\(/g);
|
|
3098
3170
|
if (functionCalls) {
|
|
3099
3171
|
functionCalls.forEach((match) => {
|
|
3100
|
-
const
|
|
3101
|
-
|
|
3172
|
+
const parts = match.split("(");
|
|
3173
|
+
if (parts[0]) {
|
|
3174
|
+
const identifier = parts[0].trim();
|
|
3175
|
+
usedIdentifiers.add(identifier);
|
|
3176
|
+
}
|
|
3102
3177
|
});
|
|
3103
3178
|
}
|
|
3104
3179
|
if (line.includes("z.infer")) {
|
|
@@ -3116,10 +3191,10 @@ export const errorMessages = {
|
|
|
3116
3191
|
continue;
|
|
3117
3192
|
}
|
|
3118
3193
|
const importMatch = line.match(/import\s*\{([^}]+)\}/);
|
|
3119
|
-
if (importMatch) {
|
|
3194
|
+
if (importMatch?.[1]) {
|
|
3120
3195
|
const imports = importMatch[1].split(",").map((imp) => imp.trim());
|
|
3121
3196
|
const usedImports = imports.filter((imp) => usedIdentifiers.has(imp));
|
|
3122
|
-
if (usedImports.length > 0) {
|
|
3197
|
+
if (usedImports.length > 0 && importMatch[1]) {
|
|
3123
3198
|
cleanedLines.push(line.replace(importMatch[1], usedImports.join(", ")));
|
|
3124
3199
|
} else {
|
|
3125
3200
|
continue;
|
|
@@ -3166,7 +3241,7 @@ var ActionGenerator = class {
|
|
|
3166
3241
|
if (!this.documentationEnabled) {
|
|
3167
3242
|
return "";
|
|
3168
3243
|
}
|
|
3169
|
-
const summary = endpoint.summary
|
|
3244
|
+
const summary = endpoint.summary ?? endpoint.description ?? `${endpoint.method} ${endpoint.path}`;
|
|
3170
3245
|
return `/**
|
|
3171
3246
|
* ${summary}
|
|
3172
3247
|
* @generated from ${endpoint.method} ${endpoint.path}
|
|
@@ -3308,7 +3383,7 @@ ${" ".repeat(indent + 2)}error: error instanceof Error ? error.message : 'Unknow
|
|
|
3308
3383
|
${" ".repeat(indent)}})`;
|
|
3309
3384
|
}
|
|
3310
3385
|
buildImportPath(relativePath) {
|
|
3311
|
-
const outputDirectory = this.configuration.outputDir
|
|
3386
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
3312
3387
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
3313
3388
|
let importBasePath = outputDirectory;
|
|
3314
3389
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -3316,7 +3391,7 @@ ${" ".repeat(indent)}})`;
|
|
|
3316
3391
|
}
|
|
3317
3392
|
return `@/${importBasePath}/${cleanPath}`;
|
|
3318
3393
|
}
|
|
3319
|
-
|
|
3394
|
+
generate(context) {
|
|
3320
3395
|
const { schema, config } = context;
|
|
3321
3396
|
const generatedFiles = [];
|
|
3322
3397
|
const endpointsByTag = this.groupEndpointsByTag(schema.endpoints);
|
|
@@ -3410,7 +3485,7 @@ ${actions}`;
|
|
|
3410
3485
|
dependencies: Object.keys(endpointsByTag).map((tag) => `./${toValidIdentifier(tag)}`)
|
|
3411
3486
|
}
|
|
3412
3487
|
});
|
|
3413
|
-
return generatedFiles;
|
|
3488
|
+
return Promise.resolve(generatedFiles);
|
|
3414
3489
|
}
|
|
3415
3490
|
getOperationName(endpoint) {
|
|
3416
3491
|
if (endpoint.operationId) {
|
|
@@ -3423,7 +3498,7 @@ ${actions}`;
|
|
|
3423
3498
|
const method = this.toPascalCase(endpoint.method.toLowerCase());
|
|
3424
3499
|
return [...pathParts, method].join("");
|
|
3425
3500
|
}
|
|
3426
|
-
generateOptimizedServerAction(endpoint,
|
|
3501
|
+
generateOptimizedServerAction(endpoint, _config) {
|
|
3427
3502
|
const actionName = toActionName(endpoint.operationId || endpoint.id);
|
|
3428
3503
|
const operationName = this.getOperationName(endpoint);
|
|
3429
3504
|
const hasRequestBody = !!endpoint.requestBody;
|
|
@@ -3464,10 +3539,15 @@ ${actions}`;
|
|
|
3464
3539
|
parameterProcessing = "";
|
|
3465
3540
|
requestOptionsParams = "";
|
|
3466
3541
|
}
|
|
3467
|
-
const
|
|
3542
|
+
const schemaConstantName = hasRequestBody && hasAnyParams ? `${operationName}InputSchema` : null;
|
|
3543
|
+
const schemaConstantDefinition = schemaConstantName ? `const ${schemaConstantName} = z.object({ body: ${operationName}RequestSchema, params: ${operationName}ParamsSchema })
|
|
3544
|
+
|
|
3545
|
+
` : "";
|
|
3546
|
+
const parsedInputType = hasRequestBody || hasAnyParams ? schemaConstantName ? `z.infer<typeof ${schemaConstantName}>` : `z.infer<typeof ${schemaName}>` : "void";
|
|
3468
3547
|
const ctxType = requiresAuth || hasRateLimit ? "{ user?: any; ratelimit?: { remaining: number } }" : "any";
|
|
3469
3548
|
const ctxDeclaration = requiresAuth || hasRateLimit ? `ctx: ${ctxType}` : "ctx?: any";
|
|
3470
|
-
const actionArgsType =
|
|
3549
|
+
const actionArgsType = schemaConstantName ? `{ parsedInput: z.infer<typeof ${schemaConstantName}>; ${ctxDeclaration} }` : `{ parsedInput: ${parsedInputType}; ${ctxDeclaration} }`;
|
|
3550
|
+
const actionArgsTypeForTemplate = actionArgsType.replace(/\n/g, " ").replace(/\s+/g, " ").trim();
|
|
3471
3551
|
const clientName = requiresAuth || hasRateLimit ? "authActionClient" : "actionClientWithMeta";
|
|
3472
3552
|
const rateLimitCode = hasRateLimit ? `
|
|
3473
3553
|
${this.commentLine("Rate limiting", 6)} const { userAgent, ip } = await getClientInfo()
|
|
@@ -3490,15 +3570,15 @@ ${this.commentLine("Rate limiting", 6)} const { userAgent, ip } = await get
|
|
|
3490
3570
|
endpoint,
|
|
3491
3571
|
"React cache, input validation, error handling"
|
|
3492
3572
|
);
|
|
3493
|
-
return `${docBlock}export const ${actionName} = cache(
|
|
3573
|
+
return `${docBlock}${schemaConstantDefinition}export const ${actionName} = cache(
|
|
3494
3574
|
${clientName}
|
|
3495
3575
|
.metadata({
|
|
3496
3576
|
name: "${kebabActionName}",
|
|
3497
3577
|
requiresAuth: ${requiresAuth}${hasRateLimit ? `,
|
|
3498
3578
|
rateLimit: { requests: ${endpoint.metadata.rateLimit?.requests || 10}, window: "${endpoint.metadata.rateLimit?.window || "1m"}" }` : ""}
|
|
3499
3579
|
})
|
|
3500
|
-
.schema(${
|
|
3501
|
-
.action(async ({ parsedInput, ctx }: ${
|
|
3580
|
+
.schema(${schemaConstantName || schemaName})
|
|
3581
|
+
.action(async ({ parsedInput, ctx }: ${actionArgsTypeForTemplate}) => {
|
|
3502
3582
|
const startTime = Date.now()
|
|
3503
3583
|
|
|
3504
3584
|
try {${rateLimitCode}${parameterProcessing}
|
|
@@ -3557,14 +3637,14 @@ ${this.commentLine("Process file with compression and validation if enabled", 10
|
|
|
3557
3637
|
endpoint,
|
|
3558
3638
|
"Input validation, revalidation, error handling"
|
|
3559
3639
|
);
|
|
3560
|
-
return `${mutationDocBlock}export const ${actionName} = ${clientName}
|
|
3640
|
+
return `${mutationDocBlock}${schemaConstantDefinition}export const ${actionName} = ${clientName}
|
|
3561
3641
|
.metadata({
|
|
3562
3642
|
name: "${kebabActionName}",
|
|
3563
3643
|
requiresAuth: ${requiresAuth}${hasRateLimit ? `,
|
|
3564
3644
|
rateLimit: { requests: ${endpoint.metadata.rateLimit?.requests || 10}, window: "${endpoint.metadata.rateLimit?.window || "1m"}" }` : ""}
|
|
3565
3645
|
})
|
|
3566
|
-
.schema(${
|
|
3567
|
-
.action(async ({ parsedInput, ctx }: ${
|
|
3646
|
+
.schema(${schemaConstantName || schemaName})
|
|
3647
|
+
.action(async ({ parsedInput, ctx }: ${actionArgsTypeForTemplate}) => {
|
|
3568
3648
|
const startTime = Date.now()
|
|
3569
3649
|
|
|
3570
3650
|
try {${rateLimitCode}${parameterProcessing}${fileUploadCode}
|
|
@@ -3692,7 +3772,7 @@ var HookGenerator = class {
|
|
|
3692
3772
|
__name(this, "HookGenerator");
|
|
3693
3773
|
}
|
|
3694
3774
|
buildImportPath(relativePath) {
|
|
3695
|
-
const outputDirectory = this.configuration.outputDir
|
|
3775
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
3696
3776
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
3697
3777
|
let importBasePath = outputDirectory;
|
|
3698
3778
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -3700,7 +3780,7 @@ var HookGenerator = class {
|
|
|
3700
3780
|
}
|
|
3701
3781
|
return `@/${importBasePath}/${cleanPath}`;
|
|
3702
3782
|
}
|
|
3703
|
-
|
|
3783
|
+
generate(context) {
|
|
3704
3784
|
const { schema } = context;
|
|
3705
3785
|
const generatedFiles = [];
|
|
3706
3786
|
const endpointsByTag = this.groupEndpointsByTag(schema.endpoints);
|
|
@@ -3833,7 +3913,7 @@ ${Object.keys(endpointsByTag).map((tag) => `export * from './${toValidIdentifier
|
|
|
3833
3913
|
dependencies: ["./useBridgeQuery", ...Object.keys(endpointsByTag).map((tag) => `./${toValidIdentifier(tag)}`)]
|
|
3834
3914
|
}
|
|
3835
3915
|
});
|
|
3836
|
-
return generatedFiles;
|
|
3916
|
+
return Promise.resolve(generatedFiles);
|
|
3837
3917
|
}
|
|
3838
3918
|
getOperationName(endpoint) {
|
|
3839
3919
|
if (endpoint.operationId) {
|
|
@@ -3850,44 +3930,65 @@ ${Object.keys(endpointsByTag).map((tag) => `export * from './${toValidIdentifier
|
|
|
3850
3930
|
const hookName = toHookName(endpoint.operationId || endpoint.id, true);
|
|
3851
3931
|
const actionName = toActionName(endpoint.operationId || endpoint.id);
|
|
3852
3932
|
const operationName = this.getOperationName(endpoint);
|
|
3853
|
-
const queryKey = this.generateQueryKey(endpoint);
|
|
3854
3933
|
const staleTime = this.getStaleTime(endpoint);
|
|
3855
3934
|
const pathParameters = endpoint.parameters.filter((parameter) => parameter.in === "path");
|
|
3856
3935
|
const queryParameters = endpoint.parameters.filter((parameter) => parameter.in === "query");
|
|
3857
3936
|
const hasPageParam = queryParameters.some((param) => param.name === "page");
|
|
3858
3937
|
const hasSearchParams = endpoint.path.includes("search") || endpoint.path.includes("list");
|
|
3859
|
-
|
|
3860
|
-
const
|
|
3938
|
+
const pathParamTypes = [];
|
|
3939
|
+
const queryParamTypes = [];
|
|
3861
3940
|
pathParameters.forEach((param) => {
|
|
3862
3941
|
const isRequired = param.required ? "" : "?";
|
|
3863
3942
|
const paramType = this.getTypeFromZodSchema(param.schema);
|
|
3864
|
-
|
|
3943
|
+
pathParamTypes.push(`${param.name}${isRequired}: ${paramType}`);
|
|
3865
3944
|
});
|
|
3866
3945
|
queryParameters.forEach((param) => {
|
|
3867
3946
|
const isRequired = param.required ? "" : "?";
|
|
3868
3947
|
const paramType = this.getTypeFromZodSchema(param.schema);
|
|
3869
|
-
|
|
3948
|
+
queryParamTypes.push(`${param.name}${isRequired}: ${paramType}`);
|
|
3870
3949
|
});
|
|
3871
3950
|
const returnType = `z.infer<typeof ${operationName}ResponseSchema>`;
|
|
3872
3951
|
const optionsType = `{ enabled?: boolean; suspense?: boolean; refetchInterval?: number; initialData?: ${returnType} }`;
|
|
3873
|
-
const
|
|
3874
|
-
|
|
3952
|
+
const paramsTypeParts = [];
|
|
3953
|
+
if (pathParameters.length > 0) {
|
|
3954
|
+
paramsTypeParts.push(`path: { ${pathParamTypes.join("; ")} }`);
|
|
3955
|
+
}
|
|
3956
|
+
if (queryParameters.length > 0) {
|
|
3957
|
+
paramsTypeParts.push(`query?: { ${queryParamTypes.join("; ")} }`);
|
|
3958
|
+
}
|
|
3959
|
+
const paramsType = paramsTypeParts.length > 0 ? `{ ${paramsTypeParts.join("; ")} }` : "{}";
|
|
3960
|
+
const enabledCondition = pathParameters.length > 0 && pathParameters[0] ? `!!params?.path?.${pathParameters[0].name}` : "true";
|
|
3961
|
+
const pathParamsBuild = pathParameters.length > 0 ? `{ ${pathParameters.map((p) => `${p.name}: params.path.${p.name}`).join(", ")} }` : "{}";
|
|
3962
|
+
const queryParamsBuild = queryParameters.length > 0 ? `{ ${queryParameters.map((p) => `${p.name}: params?.query?.${p.name}`).join(", ")} }` : "{}";
|
|
3875
3963
|
if (hasSearchParams) {
|
|
3876
|
-
const
|
|
3964
|
+
const queryParamsWithFallback = queryParameters.length > 0 ? `const queryParams = {
|
|
3965
|
+
${queryParameters.map((param) => {
|
|
3966
|
+
if (param.name === "query") {
|
|
3967
|
+
return `${param.name}: params?.query?.${param.name} || searchParams.search || ''`;
|
|
3968
|
+
} else if (param.name === "limit") {
|
|
3969
|
+
return `${param.name}: params?.query?.${param.name} !== undefined ? params.query.${param.name} : searchParams.limit`;
|
|
3970
|
+
} else {
|
|
3971
|
+
return `${param.name}: params?.query?.${param.name}`;
|
|
3972
|
+
}
|
|
3973
|
+
}).join(",\n ")}
|
|
3974
|
+
}` : "const queryParams = {}";
|
|
3877
3975
|
return `/**
|
|
3878
3976
|
* Optimized query hook for ${endpoint.method} ${endpoint.path}
|
|
3879
3977
|
* Features: URL state sync, infinite loading, optimistic updates
|
|
3978
|
+
* @param params - Named parameters object with path and query parameters
|
|
3979
|
+
* @param options - Query options
|
|
3880
3980
|
* @returns useQuery result with data of type ${returnType}
|
|
3881
3981
|
*/
|
|
3882
|
-
export function ${hookName}(${
|
|
3982
|
+
export function ${hookName}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
3883
3983
|
const [searchParams] = useQueryStates(searchParamsParser)
|
|
3884
3984
|
const { initialData, ...restOptions } = options ?? {}
|
|
3885
3985
|
|
|
3886
3986
|
return useQuery({
|
|
3887
|
-
queryKey: [
|
|
3987
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query, searchParams],
|
|
3888
3988
|
queryFn: async ({ signal }: { signal?: AbortSignal }) => {
|
|
3889
3989
|
try {
|
|
3890
|
-
|
|
3990
|
+
${queryParamsWithFallback}
|
|
3991
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: queryParams }` : "{ query: queryParams }"}))
|
|
3891
3992
|
return result
|
|
3892
3993
|
} catch (error) {
|
|
3893
3994
|
handleActionError(error)
|
|
@@ -3915,29 +4016,33 @@ export function ${hookName}(${parameterTypes.length > 0 ? `${parameterTypes.join
|
|
|
3915
4016
|
|
|
3916
4017
|
/**
|
|
3917
4018
|
* Infinite query version for paginated ${endpoint.path}
|
|
4019
|
+
* @param params - Named parameters object with path and query parameters
|
|
4020
|
+
* @param options - Query options
|
|
3918
4021
|
* @returns useInfiniteQuery result with data of type ${returnType}
|
|
3919
4022
|
*/
|
|
3920
|
-
export function ${hookName.replace("use", "useInfinite")}(${
|
|
4023
|
+
export function ${hookName.replace("use", "useInfinite")}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
3921
4024
|
const [searchParams] = useQueryStates(searchParamsParser)
|
|
3922
4025
|
const { initialData, ...restOptions } = options ?? {}
|
|
3923
4026
|
|
|
3924
4027
|
return useInfiniteQuery({
|
|
3925
|
-
queryKey: [
|
|
4028
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query, 'infinite', searchParams],
|
|
3926
4029
|
initialPageParam: 1,
|
|
3927
4030
|
queryFn: async ({ pageParam = 1, signal }: { pageParam?: number; signal?: AbortSignal }) => {
|
|
3928
4031
|
try {
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
${queryParameters.map((param) => {
|
|
4032
|
+
const queryParams = {
|
|
4033
|
+
${queryParameters.map((param) => {
|
|
3932
4034
|
if (param.name === "page" && hasPageParam) {
|
|
3933
|
-
return
|
|
4035
|
+
return `${param.name}: pageParam`;
|
|
4036
|
+
} else if (param.name === "query") {
|
|
4037
|
+
return `${param.name}: params?.query?.${param.name} || searchParams.search || ''`;
|
|
3934
4038
|
} else if (param.name === "limit") {
|
|
3935
|
-
return
|
|
4039
|
+
return `${param.name}: params?.query?.${param.name} !== undefined ? params.query.${param.name} : searchParams.limit`;
|
|
3936
4040
|
} else {
|
|
3937
|
-
return
|
|
4041
|
+
return `${param.name}: params?.query?.${param.name}`;
|
|
4042
|
+
}
|
|
4043
|
+
}).join(",\n ")}
|
|
3938
4044
|
}
|
|
3939
|
-
|
|
3940
|
-
const result = await resolveActionResult<${returnType}>(${actionName}(${actionCallParams.replace(queryParamObject, "queryParams")}))
|
|
4045
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: queryParams }` : "{ query: queryParams }"}))
|
|
3941
4046
|
return result
|
|
3942
4047
|
} catch (error) {
|
|
3943
4048
|
handleActionError(error)
|
|
@@ -3979,15 +4084,17 @@ export function ${hookName.replace("use", "useInfinite")}(${parameterTypes.lengt
|
|
|
3979
4084
|
|
|
3980
4085
|
/**
|
|
3981
4086
|
* Suspense version for ${endpoint.path} - use in Server Components
|
|
4087
|
+
* @param params - Named parameters object with path and query parameters
|
|
4088
|
+
* @param options - Query options
|
|
3982
4089
|
* @returns useSuspenseQuery result with data of type ${returnType}
|
|
3983
4090
|
*/
|
|
3984
|
-
export function ${hookName.replace("use", "useSuspense")}(${
|
|
4091
|
+
export function ${hookName.replace("use", "useSuspense")}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
3985
4092
|
const { initialData, ...restOptions } = options ?? {}
|
|
3986
4093
|
|
|
3987
4094
|
return useSuspenseQuery({
|
|
3988
|
-
queryKey: ${
|
|
4095
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query],
|
|
3989
4096
|
queryFn: async () => {
|
|
3990
|
-
const result = await resolveActionResult<${returnType}>(${actionName}(${
|
|
4097
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: ${queryParamsBuild} }` : `{ query: ${queryParamsBuild} }`}))
|
|
3991
4098
|
return result
|
|
3992
4099
|
},
|
|
3993
4100
|
staleTime: ${staleTime},
|
|
@@ -3999,16 +4106,18 @@ export function ${hookName.replace("use", "useSuspense")}(${parameterTypes.lengt
|
|
|
3999
4106
|
return `/**
|
|
4000
4107
|
* Optimized query hook for ${endpoint.method} ${endpoint.path}
|
|
4001
4108
|
* Features: Smart caching, error handling, type safety
|
|
4109
|
+
* @param params - Named parameters object with path and query parameters
|
|
4110
|
+
* @param options - Query options
|
|
4002
4111
|
* @returns useQuery result with data of type ${returnType}
|
|
4003
4112
|
*/
|
|
4004
|
-
export function ${hookName}(${
|
|
4113
|
+
export function ${hookName}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
4005
4114
|
const { initialData, ...restOptions } = options ?? {}
|
|
4006
4115
|
|
|
4007
4116
|
return useQuery({
|
|
4008
|
-
queryKey: ${
|
|
4117
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query],
|
|
4009
4118
|
queryFn: async ({ signal }: { signal?: AbortSignal }) => {
|
|
4010
4119
|
try {
|
|
4011
|
-
const result = await resolveActionResult<${returnType}>(${
|
|
4120
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: ${queryParamsBuild} }` : `{ query: ${queryParamsBuild} }`}))
|
|
4012
4121
|
return result
|
|
4013
4122
|
} catch (error) {
|
|
4014
4123
|
handleActionError(error)
|
|
@@ -4036,15 +4145,17 @@ export function ${hookName}(${parameterTypes.length > 0 ? `${parameterTypes.join
|
|
|
4036
4145
|
|
|
4037
4146
|
/**
|
|
4038
4147
|
* Suspense version for ${endpoint.path}
|
|
4148
|
+
* @param params - Named parameters object with path and query parameters
|
|
4149
|
+
* @param options - Query options
|
|
4039
4150
|
* @returns useSuspenseQuery result with data of type ${returnType}
|
|
4040
4151
|
*/
|
|
4041
|
-
export function ${hookName.replace("use", "useSuspense")}(${
|
|
4152
|
+
export function ${hookName.replace("use", "useSuspense")}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
4042
4153
|
const { initialData, ...restOptions } = options ?? {}
|
|
4043
4154
|
|
|
4044
4155
|
return useSuspenseQuery({
|
|
4045
|
-
queryKey: ${
|
|
4156
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query],
|
|
4046
4157
|
queryFn: async () => {
|
|
4047
|
-
const result = await resolveActionResult<${returnType}>(${
|
|
4158
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: ${queryParamsBuild} }` : `{ query: ${queryParamsBuild} }`}))
|
|
4048
4159
|
return result
|
|
4049
4160
|
},
|
|
4050
4161
|
staleTime: ${staleTime},
|
|
@@ -4159,7 +4270,7 @@ ${invalidationCode}
|
|
|
4159
4270
|
* Find related queries that should be invalidated when this mutation runs
|
|
4160
4271
|
*/
|
|
4161
4272
|
findRelatedQueries(endpoint, context) {
|
|
4162
|
-
if (!context?.schema
|
|
4273
|
+
if (!context?.schema.endpoints) {
|
|
4163
4274
|
return [];
|
|
4164
4275
|
}
|
|
4165
4276
|
const relatedQueries = [];
|
|
@@ -4293,7 +4404,6 @@ ${invalidationCode}
|
|
|
4293
4404
|
buildActionCallParams(endpoint, isMutation) {
|
|
4294
4405
|
const pathParameters = endpoint.parameters.filter((parameter) => parameter.in === "path");
|
|
4295
4406
|
const queryParameters = endpoint.parameters.filter((parameter) => parameter.in === "query");
|
|
4296
|
-
!!endpoint.requestBody;
|
|
4297
4407
|
if (isMutation) {
|
|
4298
4408
|
return "variables";
|
|
4299
4409
|
}
|
|
@@ -4374,13 +4484,13 @@ ${invalidationCode}
|
|
|
4374
4484
|
import { useQuery, useMutation, useInfiniteQuery, useSuspenseQuery, type UseQueryOptions, type UseMutationOptions, type UseInfiniteQueryOptions, type UseSuspenseQueryOptions, type QueryKey, type QueryFunction } from '@tanstack/react-query'
|
|
4375
4485
|
|
|
4376
4486
|
/**
|
|
4377
|
-
* Enhanced React Query wrapper hook with Next.js 16.0.
|
|
4487
|
+
* Enhanced React Query wrapper hook with Next.js 16.0.7 optimizations
|
|
4378
4488
|
* Provides consistent defaults across all queries following best practices
|
|
4379
4489
|
*
|
|
4380
4490
|
* Features:
|
|
4381
4491
|
* - React Query v5: Uses gcTime instead of cacheTime
|
|
4382
4492
|
* - React Query v5: Uses placeholderData instead of keepPreviousData
|
|
4383
|
-
* - Next.js 16.0.
|
|
4493
|
+
* - Next.js 16.0.7: Optimized for App Router and Server Components
|
|
4384
4494
|
*/
|
|
4385
4495
|
export function useBridgeQuery<TData = unknown, TError = Error>(
|
|
4386
4496
|
queryKey: QueryKey,
|
|
@@ -4395,14 +4505,8 @@ export function useBridgeQuery<TData = unknown, TError = Error>(
|
|
|
4395
4505
|
refetchOnWindowFocus: true,
|
|
4396
4506
|
refetchOnReconnect: true,
|
|
4397
4507
|
refetchOnMount: 'always',
|
|
4398
|
-
// React Query v5: placeholderData
|
|
4399
|
-
|
|
4400
|
-
placeholderData: (previousData) => {
|
|
4401
|
-
if (previousData === undefined) return undefined
|
|
4402
|
-
// Type guard: ensure we don't return a function (React Query v5 requirement)
|
|
4403
|
-
if (typeof previousData === 'function') return undefined
|
|
4404
|
-
return previousData
|
|
4405
|
-
} as any,
|
|
4508
|
+
// React Query v5: placeholderData returns previous data if available
|
|
4509
|
+
placeholderData: (previousData) => previousData,
|
|
4406
4510
|
retry: (failureCount: number, error: TError) => {
|
|
4407
4511
|
if (error instanceof Error && error.message.includes('4')) return false
|
|
4408
4512
|
return failureCount < 3
|
|
@@ -4415,15 +4519,17 @@ export function useBridgeQuery<TData = unknown, TError = Error>(
|
|
|
4415
4519
|
* Enhanced infinite query wrapper
|
|
4416
4520
|
* React Query v5: Optimized for paginated data with infinite scrolling
|
|
4417
4521
|
*/
|
|
4418
|
-
export function useBridgeInfiniteQuery<TData = unknown, TError = Error>(
|
|
4522
|
+
export function useBridgeInfiniteQuery<TData = unknown, TError = Error, TPageParam = number>(
|
|
4419
4523
|
queryKey: QueryKey,
|
|
4420
|
-
queryFn: QueryFunction<TData, QueryKey>,
|
|
4421
|
-
options?: Partial<UseInfiniteQueryOptions<TData, TError, TData, QueryKey>>
|
|
4524
|
+
queryFn: QueryFunction<TData, QueryKey, TPageParam>,
|
|
4525
|
+
options?: Partial<UseInfiniteQueryOptions<TData, TError, TData, QueryKey, TPageParam>> & {
|
|
4526
|
+
getNextPageParam?: (lastPage: TData, allPages: TData[]) => TPageParam | undefined
|
|
4527
|
+
}
|
|
4422
4528
|
) {
|
|
4423
|
-
return useInfiniteQuery<TData, TError>({
|
|
4529
|
+
return useInfiniteQuery<TData, TError, TData, QueryKey, TPageParam>({
|
|
4424
4530
|
queryKey,
|
|
4425
|
-
initialPageParam: 1,
|
|
4426
|
-
queryFn
|
|
4531
|
+
initialPageParam: 1 as TPageParam,
|
|
4532
|
+
queryFn,
|
|
4427
4533
|
staleTime: 5 * 60 * 1000,
|
|
4428
4534
|
gcTime: 10 * 60 * 1000,
|
|
4429
4535
|
refetchOnWindowFocus: true,
|
|
@@ -4433,13 +4539,15 @@ export function useBridgeInfiniteQuery<TData = unknown, TError = Error>(
|
|
|
4433
4539
|
if (error instanceof Error && error.message.includes('4')) return false
|
|
4434
4540
|
return failureCount < 3
|
|
4435
4541
|
},
|
|
4542
|
+
// Provide default getNextPageParam if not provided
|
|
4543
|
+
getNextPageParam: options?.getNextPageParam || (() => undefined),
|
|
4436
4544
|
...options,
|
|
4437
4545
|
})
|
|
4438
4546
|
}
|
|
4439
4547
|
|
|
4440
4548
|
/**
|
|
4441
4549
|
* Enhanced suspense query wrapper
|
|
4442
|
-
* Next.js 16.0.
|
|
4550
|
+
* Next.js 16.0.7: Optimized for Server Components with Suspense
|
|
4443
4551
|
*/
|
|
4444
4552
|
export function useBridgeSuspenseQuery<TData = unknown, TError = Error>(
|
|
4445
4553
|
queryKey: QueryKey,
|
|
@@ -4495,7 +4603,7 @@ var AuthGenerator = class {
|
|
|
4495
4603
|
__name(this, "AuthGenerator");
|
|
4496
4604
|
}
|
|
4497
4605
|
buildImportPath(relativePath) {
|
|
4498
|
-
const outputDirectory = this.configuration.outputDir
|
|
4606
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
4499
4607
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
4500
4608
|
let importBasePath = outputDirectory;
|
|
4501
4609
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -4503,11 +4611,11 @@ var AuthGenerator = class {
|
|
|
4503
4611
|
}
|
|
4504
4612
|
return `@/${importBasePath}/${cleanPath}`;
|
|
4505
4613
|
}
|
|
4506
|
-
|
|
4614
|
+
generate(context) {
|
|
4507
4615
|
const { config, schema } = context;
|
|
4508
4616
|
const authConfig = config.auth;
|
|
4509
4617
|
if (!authConfig?.enabled) {
|
|
4510
|
-
return [];
|
|
4618
|
+
return Promise.resolve([]);
|
|
4511
4619
|
}
|
|
4512
4620
|
const files = [];
|
|
4513
4621
|
const authEndpoints = this.findAuthEndpoints(schema.endpoints);
|
|
@@ -4522,7 +4630,7 @@ var AuthGenerator = class {
|
|
|
4522
4630
|
files.push(...this.generateAuthComponents(authEndpoints));
|
|
4523
4631
|
files.push(this.generateAuthMiddleware());
|
|
4524
4632
|
files.push(this.generateAuthContext());
|
|
4525
|
-
return files;
|
|
4633
|
+
return Promise.resolve(files);
|
|
4526
4634
|
}
|
|
4527
4635
|
findAuthEndpoints(endpoints) {
|
|
4528
4636
|
const authEndpoints = {};
|
|
@@ -4555,12 +4663,12 @@ var AuthGenerator = class {
|
|
|
4555
4663
|
}
|
|
4556
4664
|
return userEndpoints;
|
|
4557
4665
|
}
|
|
4558
|
-
generateAuthSchemas(
|
|
4666
|
+
generateAuthSchemas(_authEndpoints, _schema) {
|
|
4559
4667
|
this.buildImportPath("schemas");
|
|
4560
|
-
const loginRequestSchema =
|
|
4668
|
+
const loginRequestSchema = _authEndpoints.login?.requestBody?.content?.find(
|
|
4561
4669
|
(c) => c.type === "application/json"
|
|
4562
4670
|
)?.schema;
|
|
4563
|
-
const loginResponseSchema =
|
|
4671
|
+
const loginResponseSchema = _authEndpoints.login?.responses?.find(
|
|
4564
4672
|
(r) => r.statusCode === "200"
|
|
4565
4673
|
)?.content?.[0]?.schema;
|
|
4566
4674
|
const content = `import { z } from "zod"
|
|
@@ -4661,7 +4769,7 @@ export type Session = z.infer<typeof sessionSchema>`;
|
|
|
4661
4769
|
}
|
|
4662
4770
|
};
|
|
4663
4771
|
}
|
|
4664
|
-
generateAuthTypes(
|
|
4772
|
+
generateAuthTypes(_authEndpoints, _schema) {
|
|
4665
4773
|
const schemasImport = this.buildImportPath("auth/schemas");
|
|
4666
4774
|
const content = `// Auto-generated authentication types
|
|
4667
4775
|
import type {
|
|
@@ -5207,7 +5315,7 @@ export const useRequireAuth = (redirectTo?: string) => {
|
|
|
5207
5315
|
}
|
|
5208
5316
|
};
|
|
5209
5317
|
}
|
|
5210
|
-
generateAuthActions(
|
|
5318
|
+
generateAuthActions(_authEndpoints, _userEndpoints) {
|
|
5211
5319
|
const safeActionImport = this.buildImportPath("lib/safe-action");
|
|
5212
5320
|
const schemasImport = this.buildImportPath("auth/schemas");
|
|
5213
5321
|
const clientImport = this.buildImportPath("auth/client");
|
|
@@ -5439,7 +5547,7 @@ export const useCredentialsLogin = () => {
|
|
|
5439
5547
|
}
|
|
5440
5548
|
};
|
|
5441
5549
|
}
|
|
5442
|
-
generateAuthComponents(
|
|
5550
|
+
generateAuthComponents(_authEndpoints) {
|
|
5443
5551
|
const loginForm = {
|
|
5444
5552
|
path: "auth/components/login-form.tsx",
|
|
5445
5553
|
content: `"use client"
|
|
@@ -5559,7 +5667,6 @@ export function LoginForm() {
|
|
|
5559
5667
|
return [loginForm];
|
|
5560
5668
|
}
|
|
5561
5669
|
generateAuthMiddleware() {
|
|
5562
|
-
this.buildImportPath("auth/utils");
|
|
5563
5670
|
const content = `import { NextResponse } from "next/server"
|
|
5564
5671
|
import type { NextRequest } from "next/server"
|
|
5565
5672
|
|
|
@@ -5927,9 +6034,10 @@ var SchemaAnalyzer = class {
|
|
|
5927
6034
|
}
|
|
5928
6035
|
if (response.description) {
|
|
5929
6036
|
const errorMatch = response.description.match(/error[_\s]?code[:\s]+(\w+)/i);
|
|
5930
|
-
if (errorMatch) {
|
|
5931
|
-
|
|
5932
|
-
|
|
6037
|
+
if (errorMatch?.[1]) {
|
|
6038
|
+
const errorCode = errorMatch[1];
|
|
6039
|
+
errorCodes.set(errorCode.toLowerCase(), {
|
|
6040
|
+
code: errorCode,
|
|
5933
6041
|
status,
|
|
5934
6042
|
message: response.description
|
|
5935
6043
|
});
|
|
@@ -6015,7 +6123,7 @@ var SchemaAnalyzer = class {
|
|
|
6015
6123
|
const endpoints = {};
|
|
6016
6124
|
for (const endpoint of this.schema.endpoints) {
|
|
6017
6125
|
const path3 = endpoint.path.toLowerCase();
|
|
6018
|
-
const operationId = (endpoint.operationId
|
|
6126
|
+
const operationId = (endpoint.operationId ?? "").toLowerCase();
|
|
6019
6127
|
if ((path3.includes("/auth/login/credentials") || operationId.includes("logincredentials") || operationId.includes("signin")) && endpoint.method === "POST") {
|
|
6020
6128
|
endpoints.loginCredentials = endpoint;
|
|
6021
6129
|
} else if ((path3.includes("/auth/login") || operationId.includes("login")) && endpoint.method === "POST" && !endpoints.loginCredentials) {
|
|
@@ -6063,12 +6171,12 @@ var SchemaAnalyzer = class {
|
|
|
6063
6171
|
extractSessionConfig() {
|
|
6064
6172
|
const config = {};
|
|
6065
6173
|
const sessionEndpoints = this.schema.endpoints.filter(
|
|
6066
|
-
(e) => e.path.toLowerCase().includes("/session") || (e.operationId
|
|
6174
|
+
(e) => e.path.toLowerCase().includes("/session") || (e.operationId ?? "").toLowerCase().includes("session")
|
|
6067
6175
|
);
|
|
6068
6176
|
for (const endpoint of sessionEndpoints) {
|
|
6069
6177
|
for (const response of endpoint.responses) {
|
|
6070
6178
|
if (response.headers) {
|
|
6071
|
-
for (const [headerName,
|
|
6179
|
+
for (const [headerName, _headerDef] of Object.entries(response.headers)) {
|
|
6072
6180
|
if (headerName.toLowerCase().includes("expires") || headerName.toLowerCase().includes("max-age")) ;
|
|
6073
6181
|
}
|
|
6074
6182
|
}
|
|
@@ -6148,7 +6256,7 @@ var SchemaAnalyzer = class {
|
|
|
6148
6256
|
/**
|
|
6149
6257
|
* Check if OAuth provider is supported
|
|
6150
6258
|
*/
|
|
6151
|
-
isOAuthProviderSupported(
|
|
6259
|
+
isOAuthProviderSupported(_provider) {
|
|
6152
6260
|
const extractedEndpoints = this.extractAuthEndpoints();
|
|
6153
6261
|
return !!(extractedEndpoints.oauthAuthorize && extractedEndpoints.oauthCallback);
|
|
6154
6262
|
}
|
|
@@ -6230,7 +6338,7 @@ var SchemaAnalyzer = class {
|
|
|
6230
6338
|
errorCodes.set(code.toLowerCase(), {
|
|
6231
6339
|
code,
|
|
6232
6340
|
status,
|
|
6233
|
-
message: schema.properties?.detail?.default
|
|
6341
|
+
message: schema.properties?.detail?.default ?? schema.properties?.message?.default ?? ""
|
|
6234
6342
|
});
|
|
6235
6343
|
});
|
|
6236
6344
|
}
|
|
@@ -6239,9 +6347,10 @@ var SchemaAnalyzer = class {
|
|
|
6239
6347
|
const detail = schema.properties.detail;
|
|
6240
6348
|
if (typeof detail === "string") {
|
|
6241
6349
|
const codeMatch = detail.match(/(\w+)[_\s]?error/i);
|
|
6242
|
-
if (codeMatch) {
|
|
6243
|
-
|
|
6244
|
-
|
|
6350
|
+
if (codeMatch?.[1]) {
|
|
6351
|
+
const errorCode = codeMatch[1];
|
|
6352
|
+
errorCodes.set(errorCode.toLowerCase(), {
|
|
6353
|
+
code: errorCode,
|
|
6245
6354
|
status,
|
|
6246
6355
|
message: detail
|
|
6247
6356
|
});
|
|
@@ -6253,11 +6362,11 @@ var SchemaAnalyzer = class {
|
|
|
6253
6362
|
if (!schema || typeof schema !== "object") return;
|
|
6254
6363
|
if (schema.properties?.file) {
|
|
6255
6364
|
const fileSchema = schema.properties.file;
|
|
6256
|
-
if (fileSchema.maxSize
|
|
6257
|
-
config.maxSize = fileSchema.maxSize
|
|
6365
|
+
if (fileSchema.maxSize ?? fileSchema["x-max-size"]) {
|
|
6366
|
+
config.maxSize = fileSchema.maxSize ?? fileSchema["x-max-size"];
|
|
6258
6367
|
}
|
|
6259
|
-
if (fileSchema.allowedTypes
|
|
6260
|
-
config.allowedTypes = fileSchema.allowedTypes
|
|
6368
|
+
if (fileSchema.allowedTypes ?? fileSchema["x-allowed-types"]) {
|
|
6369
|
+
config.allowedTypes = fileSchema.allowedTypes ?? fileSchema["x-allowed-types"];
|
|
6261
6370
|
}
|
|
6262
6371
|
}
|
|
6263
6372
|
if (schema["x-file-constraints"]) {
|
|
@@ -6277,7 +6386,7 @@ var NextAuthGenerator = class {
|
|
|
6277
6386
|
analyzer;
|
|
6278
6387
|
accountStatusPatterns;
|
|
6279
6388
|
buildImportPath(relativePath) {
|
|
6280
|
-
const outputDirectory = this.configuration.outputDir
|
|
6389
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
6281
6390
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
6282
6391
|
let importBasePath = outputDirectory;
|
|
6283
6392
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -6285,11 +6394,11 @@ var NextAuthGenerator = class {
|
|
|
6285
6394
|
}
|
|
6286
6395
|
return `@/${importBasePath}/${cleanPath}`;
|
|
6287
6396
|
}
|
|
6288
|
-
|
|
6397
|
+
generate(context) {
|
|
6289
6398
|
const { config, schema } = context;
|
|
6290
6399
|
const authConfig = config.auth;
|
|
6291
6400
|
if (!authConfig?.enabled || authConfig.provider !== "next-auth") {
|
|
6292
|
-
return [];
|
|
6401
|
+
return Promise.resolve([]);
|
|
6293
6402
|
}
|
|
6294
6403
|
this.analyzer = new SchemaAnalyzer(schema);
|
|
6295
6404
|
this.accountStatusPatterns = this.analyzer.extractAccountStatusPatterns();
|
|
@@ -6306,7 +6415,7 @@ var NextAuthGenerator = class {
|
|
|
6306
6415
|
console.warn("These features will be disabled in generated code.\n");
|
|
6307
6416
|
}
|
|
6308
6417
|
const detectedOAuthProviders = this.analyzer.extractOAuthProviders();
|
|
6309
|
-
const configuredOAuthProviders = authConfig.oauth?.providers
|
|
6418
|
+
const configuredOAuthProviders = authConfig.oauth?.providers ?? [];
|
|
6310
6419
|
const validOAuthProviders = configuredOAuthProviders.filter(
|
|
6311
6420
|
(provider) => this.analyzer.isOAuthProviderSupported(provider)
|
|
6312
6421
|
);
|
|
@@ -6317,7 +6426,7 @@ var NextAuthGenerator = class {
|
|
|
6317
6426
|
console.warn(
|
|
6318
6427
|
`\u26A0\uFE0F NextAuth is enabled but login endpoints are not found in OpenAPI schema. Expected endpoints: /auth/login or /auth/login/credentials. NextAuth configuration will not be generated.`
|
|
6319
6428
|
);
|
|
6320
|
-
return files;
|
|
6429
|
+
return Promise.resolve(files);
|
|
6321
6430
|
}
|
|
6322
6431
|
files.push(this.generateNextAuthConfig(
|
|
6323
6432
|
extractedEndpoints,
|
|
@@ -6330,7 +6439,7 @@ var NextAuthGenerator = class {
|
|
|
6330
6439
|
if (hasPasskeys) {
|
|
6331
6440
|
files.push(this.generatePasskeyUtils(extractedEndpoints));
|
|
6332
6441
|
}
|
|
6333
|
-
return files;
|
|
6442
|
+
return Promise.resolve(files);
|
|
6334
6443
|
}
|
|
6335
6444
|
findAuthEndpoints(endpoints) {
|
|
6336
6445
|
const authEndpoints = {};
|
|
@@ -6433,14 +6542,14 @@ var NextAuthGenerator = class {
|
|
|
6433
6542
|
}
|
|
6434
6543
|
generateNextAuthConfig(extractedEndpoints, oauthProviders, hasMFA, hasPasskeys) {
|
|
6435
6544
|
const authConfig = this.configuration.auth;
|
|
6436
|
-
const apiBaseUrl = this.configuration.api
|
|
6545
|
+
const apiBaseUrl = this.configuration.api.baseUrl || "http://localhost:8000";
|
|
6437
6546
|
const apiUrl = `process.env.NEXT_PUBLIC_API_URL || '${apiBaseUrl}'`;
|
|
6438
6547
|
const hasGoogle = oauthProviders.includes("google");
|
|
6439
6548
|
const hasGitHub = oauthProviders.includes("github");
|
|
6440
6549
|
oauthProviders.includes("discord");
|
|
6441
6550
|
oauthProviders.includes("microsoft");
|
|
6442
6551
|
oauthProviders.includes("apple");
|
|
6443
|
-
const { activeStatuses, inactiveStatuses, suspendedStatuses, lockedStatuses } = this.accountStatusPatterns;
|
|
6552
|
+
const { activeStatuses, inactiveStatuses, suspendedStatuses, lockedStatuses: _lockedStatuses } = this.accountStatusPatterns;
|
|
6444
6553
|
this.analyzer.extractErrorCodes();
|
|
6445
6554
|
const sessionStrategy = authConfig.session?.strategy || "jwt";
|
|
6446
6555
|
const sessionMaxAge = authConfig.session?.maxAge || 7 * 24 * 60 * 60;
|
|
@@ -6989,7 +7098,7 @@ export async function authenticateWithPasskey(email?: string) {
|
|
|
6989
7098
|
}
|
|
6990
7099
|
};
|
|
6991
7100
|
}
|
|
6992
|
-
generateAuthTypes(
|
|
7101
|
+
generateAuthTypes(_authEndpoints, _accountStatusPatterns) {
|
|
6993
7102
|
const content = `/**
|
|
6994
7103
|
* Authentication Types
|
|
6995
7104
|
* Auto-generated by Mulink
|
|
@@ -7060,7 +7169,7 @@ export class AccountInactiveError extends AuthError {
|
|
|
7060
7169
|
}
|
|
7061
7170
|
};
|
|
7062
7171
|
}
|
|
7063
|
-
generateAuthUtils(
|
|
7172
|
+
generateAuthUtils(_authEndpoints) {
|
|
7064
7173
|
const content = `/**
|
|
7065
7174
|
* Authentication Utilities
|
|
7066
7175
|
* Auto-generated by Mulink
|
|
@@ -7113,7 +7222,7 @@ export async function requireAuth() {
|
|
|
7113
7222
|
}
|
|
7114
7223
|
};
|
|
7115
7224
|
}
|
|
7116
|
-
generatePasskeyUtils(
|
|
7225
|
+
generatePasskeyUtils(_passkeyEndpoints) {
|
|
7117
7226
|
const content = `/**
|
|
7118
7227
|
* Passkey (WebAuthn) Utilities
|
|
7119
7228
|
* Auto-generated by Mulink
|
|
@@ -7166,7 +7275,7 @@ var MiddlewareGenerator = class {
|
|
|
7166
7275
|
__name(this, "MiddlewareGenerator");
|
|
7167
7276
|
}
|
|
7168
7277
|
buildImportPath(relativePath) {
|
|
7169
|
-
const outputDirectory = this.configuration.outputDir
|
|
7278
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
7170
7279
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
7171
7280
|
let importBasePath = outputDirectory;
|
|
7172
7281
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -7174,7 +7283,7 @@ var MiddlewareGenerator = class {
|
|
|
7174
7283
|
}
|
|
7175
7284
|
return `@/${importBasePath}/${cleanPath}`;
|
|
7176
7285
|
}
|
|
7177
|
-
|
|
7286
|
+
generate(_context) {
|
|
7178
7287
|
const files = [];
|
|
7179
7288
|
files.push(this.generateMainMiddleware());
|
|
7180
7289
|
files.push(this.generateRateLimitMiddleware());
|
|
@@ -7182,7 +7291,7 @@ var MiddlewareGenerator = class {
|
|
|
7182
7291
|
files.push(this.generateSecurityMiddleware());
|
|
7183
7292
|
files.push(this.generateLoggingMiddleware());
|
|
7184
7293
|
files.push(this.generateApiMiddleware());
|
|
7185
|
-
return files;
|
|
7294
|
+
return Promise.resolve(files);
|
|
7186
7295
|
}
|
|
7187
7296
|
generateMainMiddleware() {
|
|
7188
7297
|
const authImport = this.buildImportPath("auth/middleware");
|
|
@@ -7589,7 +7698,7 @@ var UploadGenerator = class {
|
|
|
7589
7698
|
}
|
|
7590
7699
|
analyzer;
|
|
7591
7700
|
buildImportPath(relativePath) {
|
|
7592
|
-
const outputDirectory = this.configuration.outputDir
|
|
7701
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
7593
7702
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
7594
7703
|
let importBasePath = outputDirectory;
|
|
7595
7704
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -7597,19 +7706,19 @@ var UploadGenerator = class {
|
|
|
7597
7706
|
}
|
|
7598
7707
|
return `@/${importBasePath}/${cleanPath}`;
|
|
7599
7708
|
}
|
|
7600
|
-
|
|
7709
|
+
generate(context) {
|
|
7601
7710
|
const { schema } = context;
|
|
7602
7711
|
const generatedFiles = [];
|
|
7603
7712
|
this.analyzer = new SchemaAnalyzer(schema);
|
|
7604
7713
|
const uploadEndpoints = schema.endpoints.filter(
|
|
7605
|
-
(endpoint) => endpoint.requestBody?.content
|
|
7714
|
+
(endpoint) => endpoint.requestBody?.content.some(
|
|
7606
7715
|
(content) => content.type === "multipart/form-data" || content.type === "application/octet-stream"
|
|
7607
7716
|
)
|
|
7608
7717
|
);
|
|
7609
7718
|
if (uploadEndpoints.length === 0 || !this.configuration.uploads?.enabled) {
|
|
7610
|
-
return generatedFiles;
|
|
7719
|
+
return Promise.resolve(generatedFiles);
|
|
7611
7720
|
}
|
|
7612
|
-
const presignedConfig = this.configuration.uploads
|
|
7721
|
+
const presignedConfig = this.configuration.uploads.presignedUploads;
|
|
7613
7722
|
if (presignedConfig?.enabled) {
|
|
7614
7723
|
const isPresignedSupported = this.analyzer.isPresignedUploadsSupported();
|
|
7615
7724
|
if (!isPresignedSupported) {
|
|
@@ -7620,14 +7729,14 @@ var UploadGenerator = class {
|
|
|
7620
7729
|
}
|
|
7621
7730
|
const extractedUploadConfig = this.analyzer.extractUploadConfig();
|
|
7622
7731
|
const presignedEnabled = presignedConfig?.enabled && this.analyzer.isPresignedUploadsSupported();
|
|
7623
|
-
const uploadProvider = this.configuration.uploads
|
|
7624
|
-
const uploadStrategy = presignedEnabled ? "presigned" : this.configuration.uploads
|
|
7732
|
+
const uploadProvider = this.configuration.uploads.provider || (extractedUploadConfig.presignedEndpoint ? "minio" : "standard");
|
|
7733
|
+
const uploadStrategy = presignedEnabled ? "presigned" : this.configuration.uploads.strategy || "standard";
|
|
7625
7734
|
presignedEnabled ? {
|
|
7626
|
-
presignEndpoint: extractedUploadConfig.presignedEndpoint || presignedConfig
|
|
7627
|
-
fallbackToBackend: presignedConfig
|
|
7735
|
+
presignEndpoint: extractedUploadConfig.presignedEndpoint || presignedConfig.presignEndpoint || "/api/v1/files/presign-upload",
|
|
7736
|
+
fallbackToBackend: presignedConfig.fallbackToBackend !== false
|
|
7628
7737
|
} : {
|
|
7629
7738
|
};
|
|
7630
|
-
const progressConfig = this.configuration.uploads
|
|
7739
|
+
const progressConfig = this.configuration.uploads.progressTracking || {
|
|
7631
7740
|
enabled: true,
|
|
7632
7741
|
useXHR: true
|
|
7633
7742
|
};
|
|
@@ -7644,7 +7753,7 @@ var UploadGenerator = class {
|
|
|
7644
7753
|
progressConfig
|
|
7645
7754
|
));
|
|
7646
7755
|
}
|
|
7647
|
-
return generatedFiles;
|
|
7756
|
+
return Promise.resolve(generatedFiles);
|
|
7648
7757
|
}
|
|
7649
7758
|
/**
|
|
7650
7759
|
* Find presigned upload endpoint in schema
|
|
@@ -7657,7 +7766,7 @@ var UploadGenerator = class {
|
|
|
7657
7766
|
return pathLower.includes("presign") || pathLower.includes("presigned") || operationIdLower.includes("presign") || operationIdLower.includes("presigned");
|
|
7658
7767
|
}) || null;
|
|
7659
7768
|
}
|
|
7660
|
-
generateUploadUtilities(
|
|
7769
|
+
generateUploadUtilities(_uploadProvider, _uploadStrategy, _presignedEndpoint) {
|
|
7661
7770
|
const { uploads } = this.configuration;
|
|
7662
7771
|
const extractedConfig = this.analyzer.extractUploadConfig();
|
|
7663
7772
|
const maxSize = uploads?.security?.maxSize || (extractedConfig.maxSize ? `${extractedConfig.maxSize}MB` : "10MB");
|
|
@@ -8008,8 +8117,8 @@ async function uploadViaBackendApi(
|
|
|
8008
8117
|
xhr.responseType = 'json'
|
|
8009
8118
|
// Use base URL from environment or configuration
|
|
8010
8119
|
const baseUrl = typeof window !== 'undefined'
|
|
8011
|
-
? (process.env.NEXT_PUBLIC_API_URL || '${this.configuration.api
|
|
8012
|
-
: '${this.configuration.api
|
|
8120
|
+
? (process.env.NEXT_PUBLIC_API_URL || '${this.configuration.api.baseUrl || "http://localhost:8000"}')
|
|
8121
|
+
: '${this.configuration.api.baseUrl || "http://localhost:8000"}'
|
|
8013
8122
|
xhr.open('POST', \`\${baseUrl}${endpoint.path}\`)
|
|
8014
8123
|
xhr.send(formData)
|
|
8015
8124
|
})
|
|
@@ -8284,8 +8393,8 @@ export function ${hookName}Upload(options?: {
|
|
|
8284
8393
|
}
|
|
8285
8394
|
generateUseUploadFileHook(uploadProvider, uploadStrategy, context) {
|
|
8286
8395
|
const uploadUtilsImport = this.buildImportPath("services/uploadUtils");
|
|
8287
|
-
const uploadEndpoints = context?.schema
|
|
8288
|
-
(e) => e.metadata
|
|
8396
|
+
const uploadEndpoints = context?.schema.endpoints.filter(
|
|
8397
|
+
(e) => e.metadata.fileUpload || e.path.toLowerCase().includes("upload") || e.operationId && e.operationId.toLowerCase().includes("upload")
|
|
8289
8398
|
) || [];
|
|
8290
8399
|
const firstUploadEndpoint = uploadEndpoints[0];
|
|
8291
8400
|
let uploadMethodName = "uploadFile";
|
|
@@ -8425,7 +8534,7 @@ var ErrorHandlerGenerator = class {
|
|
|
8425
8534
|
}
|
|
8426
8535
|
analyzer;
|
|
8427
8536
|
buildImportPath(relativePath) {
|
|
8428
|
-
const outputDirectory = this.configuration.outputDir
|
|
8537
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
8429
8538
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
8430
8539
|
let importBasePath = outputDirectory;
|
|
8431
8540
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -8433,18 +8542,18 @@ var ErrorHandlerGenerator = class {
|
|
|
8433
8542
|
}
|
|
8434
8543
|
return `@/${importBasePath}/${cleanPath}`;
|
|
8435
8544
|
}
|
|
8436
|
-
|
|
8545
|
+
generate(context) {
|
|
8437
8546
|
const files = [];
|
|
8438
|
-
const { schema
|
|
8547
|
+
const { schema } = context;
|
|
8439
8548
|
const { api } = this.configuration;
|
|
8440
|
-
const errorHandling = api
|
|
8549
|
+
const errorHandling = api.errorHandling;
|
|
8441
8550
|
this.analyzer = new SchemaAnalyzer(schema);
|
|
8442
8551
|
const errorCodes = this.analyzer.extractErrorCodes();
|
|
8443
8552
|
const accountStatusPatterns = this.analyzer.extractAccountStatusPatterns();
|
|
8444
8553
|
const extractedAuthEndpoints = this.analyzer.extractAuthEndpoints();
|
|
8445
8554
|
const hasAuthEndpoints = !!(extractedAuthEndpoints.login || extractedAuthEndpoints.loginCredentials);
|
|
8446
8555
|
const hasAuthErrors = errorCodes.size > 0 || (accountStatusPatterns.inactiveStatuses.length > 0 || accountStatusPatterns.suspendedStatuses.length > 0);
|
|
8447
|
-
const shouldGenerate = hasAuthEndpoints && (errorHandling?.generateAuthErrorHandler !== false && errorHandling?.enableAuthErrorHandling !== false || hasAuthErrors && errorHandling
|
|
8556
|
+
const shouldGenerate = hasAuthEndpoints && (errorHandling?.generateAuthErrorHandler !== false && errorHandling?.enableAuthErrorHandling !== false || hasAuthErrors && errorHandling.generateAuthErrorHandler !== false);
|
|
8448
8557
|
if (shouldGenerate) {
|
|
8449
8558
|
files.push(this.generateAuthErrorHandler(errorCodes, accountStatusPatterns));
|
|
8450
8559
|
} else if (errorHandling?.enableAuthErrorHandling === true && !hasAuthEndpoints) {
|
|
@@ -8452,11 +8561,11 @@ var ErrorHandlerGenerator = class {
|
|
|
8452
8561
|
`\u26A0\uFE0F Auth error handling is enabled but auth endpoints are not found in OpenAPI schema. Auth error handler will not be generated.`
|
|
8453
8562
|
);
|
|
8454
8563
|
}
|
|
8455
|
-
return files;
|
|
8564
|
+
return Promise.resolve(files);
|
|
8456
8565
|
}
|
|
8457
8566
|
generateAuthErrorHandler(errorCodes, accountStatusPatterns) {
|
|
8458
8567
|
const authPath = this.configuration.auth?.authPath || "@/lib/auth";
|
|
8459
|
-
const errorHandling = this.configuration.api
|
|
8568
|
+
const errorHandling = this.configuration.api.errorHandling;
|
|
8460
8569
|
const authErrorHandlerPath = errorHandling?.authErrorHandlerPath || "@/lib/auth-error-handler";
|
|
8461
8570
|
let handlerOutputPath = authErrorHandlerPath;
|
|
8462
8571
|
if (handlerOutputPath.startsWith("@/")) {
|
|
@@ -8495,7 +8604,7 @@ async function getToast(): Promise<{
|
|
|
8495
8604
|
try {
|
|
8496
8605
|
// Try to import sonner (common toast library)
|
|
8497
8606
|
const sonner = await import('sonner')
|
|
8498
|
-
toast = sonner
|
|
8607
|
+
toast = sonner.toast
|
|
8499
8608
|
return toast
|
|
8500
8609
|
} catch {
|
|
8501
8610
|
// Fallback to console if toast library not available
|
|
@@ -8669,7 +8778,7 @@ var SSEGenerator = class {
|
|
|
8669
8778
|
}
|
|
8670
8779
|
analyzer;
|
|
8671
8780
|
buildImportPath(relativePath) {
|
|
8672
|
-
const outputDirectory = this.configuration.outputDir
|
|
8781
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
8673
8782
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
8674
8783
|
let importBasePath = outputDirectory;
|
|
8675
8784
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -8701,11 +8810,11 @@ var SSEGenerator = class {
|
|
|
8701
8810
|
);
|
|
8702
8811
|
return hasEventStream;
|
|
8703
8812
|
}
|
|
8704
|
-
|
|
8813
|
+
generate(context) {
|
|
8705
8814
|
const { schema } = context;
|
|
8706
8815
|
const generatedFiles = [];
|
|
8707
8816
|
this.analyzer = new SchemaAnalyzer(schema);
|
|
8708
|
-
const streamingConfig = this.configuration.framework
|
|
8817
|
+
const streamingConfig = this.configuration.framework.streaming;
|
|
8709
8818
|
const hasStreamingEndpoints = schema.endpoints.some((endpoint) => this.isSSEEndpoint(endpoint));
|
|
8710
8819
|
if (streamingConfig?.enabled === true && !hasStreamingEndpoints) {
|
|
8711
8820
|
console.warn(
|
|
@@ -8714,11 +8823,11 @@ var SSEGenerator = class {
|
|
|
8714
8823
|
}
|
|
8715
8824
|
const shouldEnableStreaming = streamingConfig?.enabled === true ? hasStreamingEndpoints : streamingConfig?.enabled !== false && hasStreamingEndpoints;
|
|
8716
8825
|
if (!shouldEnableStreaming || streamingConfig?.sse?.enabled === false || !hasStreamingEndpoints) {
|
|
8717
|
-
return generatedFiles;
|
|
8826
|
+
return Promise.resolve(generatedFiles);
|
|
8718
8827
|
}
|
|
8719
8828
|
const sseEndpoints = schema.endpoints.filter((endpoint) => this.isSSEEndpoint(endpoint));
|
|
8720
8829
|
if (sseEndpoints.length === 0) {
|
|
8721
|
-
return generatedFiles;
|
|
8830
|
+
return Promise.resolve(generatedFiles);
|
|
8722
8831
|
}
|
|
8723
8832
|
for (const endpoint of sseEndpoints) {
|
|
8724
8833
|
generatedFiles.push(this.generateSSEClientMethod(endpoint));
|
|
@@ -8727,7 +8836,7 @@ var SSEGenerator = class {
|
|
|
8727
8836
|
for (const [tag, tagEndpoints] of Object.entries(endpointsByTag)) {
|
|
8728
8837
|
generatedFiles.push(this.generateSSEHooks(tag, tagEndpoints));
|
|
8729
8838
|
}
|
|
8730
|
-
return generatedFiles;
|
|
8839
|
+
return Promise.resolve(generatedFiles);
|
|
8731
8840
|
}
|
|
8732
8841
|
generateSSEClientMethod(endpoint) {
|
|
8733
8842
|
const operationId = endpoint.operationId || endpoint.id;
|
|
@@ -8762,8 +8871,8 @@ var SSEGenerator = class {
|
|
|
8762
8871
|
}): EventSource => {
|
|
8763
8872
|
${pathParamNames.length > 0 ? `const { ${pathParamNames.join(", ")}, ...restOptions } = options.params || {}
|
|
8764
8873
|
` : ""}const baseUrl = typeof window !== 'undefined'
|
|
8765
|
-
? (process.env.NEXT_PUBLIC_API_URL || '${this.configuration.api
|
|
8766
|
-
: '${this.configuration.api
|
|
8874
|
+
? (process.env.NEXT_PUBLIC_API_URL || '${this.configuration.api.baseUrl || "http://localhost:8000"}')
|
|
8875
|
+
: '${this.configuration.api.baseUrl || "http://localhost:8000"}'
|
|
8767
8876
|
const sseUrl = \`\${baseUrl}${urlConstruction}\`
|
|
8768
8877
|
|
|
8769
8878
|
// Create EventSource connection
|
|
@@ -8809,7 +8918,7 @@ var SSEGenerator = class {
|
|
|
8809
8918
|
generateSSEHooks(tag, endpoints) {
|
|
8810
8919
|
const tagName = toValidIdentifier(tag);
|
|
8811
8920
|
const clientImport = this.buildImportPath("client");
|
|
8812
|
-
const streamingConfig = this.configuration.framework
|
|
8921
|
+
const streamingConfig = this.configuration.framework.streaming?.sse;
|
|
8813
8922
|
const autoReconnect = streamingConfig?.autoReconnect !== false;
|
|
8814
8923
|
const reconnectDelay = streamingConfig?.reconnectDelay || 3e3;
|
|
8815
8924
|
const maxReconnectAttempts = streamingConfig?.maxReconnectAttempts || 5;
|
|
@@ -9002,7 +9111,7 @@ var NextJsCodeGenerator = class {
|
|
|
9002
9111
|
__name(this, "NextJsCodeGenerator");
|
|
9003
9112
|
}
|
|
9004
9113
|
buildImportPath(relativePath) {
|
|
9005
|
-
const outputDirectory = this.configuration.outputDir
|
|
9114
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
9006
9115
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
9007
9116
|
let importBasePath = outputDirectory;
|
|
9008
9117
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -9016,9 +9125,9 @@ var NextJsCodeGenerator = class {
|
|
|
9016
9125
|
path: file.path,
|
|
9017
9126
|
metadata: {
|
|
9018
9127
|
...file.metadata,
|
|
9019
|
-
exports: file.metadata?.exports
|
|
9020
|
-
imports: file.metadata?.imports
|
|
9021
|
-
dependencies: file.metadata?.dependencies
|
|
9128
|
+
exports: file.metadata?.exports ?? [],
|
|
9129
|
+
imports: file.metadata?.imports ?? [],
|
|
9130
|
+
dependencies: file.metadata?.dependencies.map((dependency) => {
|
|
9022
9131
|
if (dependency.startsWith("@/generated/")) {
|
|
9023
9132
|
return dependency;
|
|
9024
9133
|
}
|
|
@@ -9027,7 +9136,7 @@ var NextJsCodeGenerator = class {
|
|
|
9027
9136
|
return `@/generated/${relativePart}`;
|
|
9028
9137
|
}
|
|
9029
9138
|
return dependency;
|
|
9030
|
-
})
|
|
9139
|
+
}) ?? []
|
|
9031
9140
|
}
|
|
9032
9141
|
}));
|
|
9033
9142
|
}
|
|
@@ -9060,11 +9169,11 @@ var NextJsCodeGenerator = class {
|
|
|
9060
9169
|
const uploadGenerator = new UploadGenerator(this.configuration);
|
|
9061
9170
|
generatedFiles.push(...await uploadGenerator.generate(context));
|
|
9062
9171
|
}
|
|
9063
|
-
if (this.configuration.api
|
|
9172
|
+
if (this.configuration.api.errorHandling?.generateAuthErrorHandler !== false) {
|
|
9064
9173
|
const errorHandlerGenerator = new ErrorHandlerGenerator(this.configuration);
|
|
9065
9174
|
generatedFiles.push(...await errorHandlerGenerator.generate(context));
|
|
9066
9175
|
}
|
|
9067
|
-
if (this.configuration.framework
|
|
9176
|
+
if (this.configuration.framework.streaming?.enabled) {
|
|
9068
9177
|
const sseGenerator = new SSEGenerator(this.configuration);
|
|
9069
9178
|
generatedFiles.push(...await sseGenerator.generate(context));
|
|
9070
9179
|
}
|
|
@@ -9074,14 +9183,14 @@ var NextJsCodeGenerator = class {
|
|
|
9074
9183
|
throw new GenerationError(`Failed to generate Next.js files: ${errorMessage}`);
|
|
9075
9184
|
}
|
|
9076
9185
|
}
|
|
9077
|
-
|
|
9186
|
+
generateSafeActionClient() {
|
|
9078
9187
|
const safeActionContent = `import { DEFAULT_SERVER_ERROR_MESSAGE, createSafeActionClient } from "next-safe-action";
|
|
9079
9188
|
import { headers } from "next/headers";
|
|
9080
9189
|
import { z } from "zod";
|
|
9081
9190
|
|
|
9082
9191
|
/**
|
|
9083
9192
|
* Enhanced Action Error class for better error handling
|
|
9084
|
-
* Follows Next.js 16.0.
|
|
9193
|
+
* Follows Next.js 16.0.7 best practices
|
|
9085
9194
|
*/
|
|
9086
9195
|
export class ActionError extends Error {
|
|
9087
9196
|
constructor(
|
|
@@ -9118,7 +9227,7 @@ export const actionClient = createSafeActionClient({
|
|
|
9118
9227
|
|
|
9119
9228
|
/**
|
|
9120
9229
|
* Enhanced action client with metadata support
|
|
9121
|
-
* Next.js 16.0.
|
|
9230
|
+
* Next.js 16.0.7: Supports metadata for better action tracking and rate limiting
|
|
9122
9231
|
*/
|
|
9123
9232
|
export const actionClientWithMeta = createSafeActionClient({
|
|
9124
9233
|
handleServerError(e) {
|
|
@@ -9139,7 +9248,7 @@ export const actionClientWithMeta = createSafeActionClient({
|
|
|
9139
9248
|
requests: z.number().int().positive().describe('Number of requests allowed'),
|
|
9140
9249
|
window: z.string().describe('Time window (e.g., "10s", "1m", "1h")'),
|
|
9141
9250
|
}).optional().describe('Rate limiting configuration'),
|
|
9142
|
-
cacheTags: z.array(z.string()).optional().describe('Cache tags for invalidation (Next.js 16.0.
|
|
9251
|
+
cacheTags: z.array(z.string()).optional().describe('Cache tags for invalidation (Next.js 16.0.7)'),
|
|
9143
9252
|
});
|
|
9144
9253
|
},
|
|
9145
9254
|
});
|
|
@@ -9230,7 +9339,7 @@ async function getCurrentUser() {
|
|
|
9230
9339
|
// Implement your authentication logic here
|
|
9231
9340
|
return null;
|
|
9232
9341
|
}`;
|
|
9233
|
-
return [
|
|
9342
|
+
return Promise.resolve([
|
|
9234
9343
|
{
|
|
9235
9344
|
path: "lib/safe-action.ts",
|
|
9236
9345
|
content: safeActionContent,
|
|
@@ -9241,7 +9350,7 @@ async function getCurrentUser() {
|
|
|
9241
9350
|
dependencies: []
|
|
9242
9351
|
}
|
|
9243
9352
|
}
|
|
9244
|
-
];
|
|
9353
|
+
]);
|
|
9245
9354
|
}
|
|
9246
9355
|
};
|
|
9247
9356
|
var FileSystemManager = class {
|
|
@@ -9249,7 +9358,7 @@ var FileSystemManager = class {
|
|
|
9249
9358
|
__name(this, "FileSystemManager");
|
|
9250
9359
|
}
|
|
9251
9360
|
async writeFile(filePath, content, options = {}) {
|
|
9252
|
-
if (!filePath
|
|
9361
|
+
if (!filePath.trim()) {
|
|
9253
9362
|
throw new Error("File path is required and cannot be empty");
|
|
9254
9363
|
}
|
|
9255
9364
|
if (typeof content !== "string") {
|
|
@@ -9286,7 +9395,7 @@ var FileSystemManager = class {
|
|
|
9286
9395
|
}
|
|
9287
9396
|
}
|
|
9288
9397
|
async readFile(filePath, encoding = "utf8") {
|
|
9289
|
-
if (!filePath
|
|
9398
|
+
if (!filePath.trim()) {
|
|
9290
9399
|
throw new Error("File path is required and cannot be empty");
|
|
9291
9400
|
}
|
|
9292
9401
|
try {
|
|
@@ -9313,7 +9422,7 @@ var FileSystemManager = class {
|
|
|
9313
9422
|
}
|
|
9314
9423
|
}
|
|
9315
9424
|
async getFileHash(filePath) {
|
|
9316
|
-
if (!filePath
|
|
9425
|
+
if (!filePath.trim()) {
|
|
9317
9426
|
throw new Error("File path is required and cannot be empty");
|
|
9318
9427
|
}
|
|
9319
9428
|
try {
|
|
@@ -9339,7 +9448,7 @@ var FileSystemManager = class {
|
|
|
9339
9448
|
}
|
|
9340
9449
|
}
|
|
9341
9450
|
async findFiles(dir, pattern, recursive = true) {
|
|
9342
|
-
if (!dir
|
|
9451
|
+
if (!dir.trim()) {
|
|
9343
9452
|
throw new Error("Directory path is required and cannot be empty");
|
|
9344
9453
|
}
|
|
9345
9454
|
if (!(pattern instanceof RegExp)) {
|
|
@@ -9382,16 +9491,52 @@ var BridgeCore = class {
|
|
|
9382
9491
|
logger;
|
|
9383
9492
|
schemaParser;
|
|
9384
9493
|
fileManager;
|
|
9494
|
+
/**
|
|
9495
|
+
* Creates a new instance of BridgeCore
|
|
9496
|
+
*
|
|
9497
|
+
* Initializes all required dependencies including logger, schema parser, and file manager.
|
|
9498
|
+
* All dependencies are readonly to ensure immutability and thread safety.
|
|
9499
|
+
*/
|
|
9385
9500
|
constructor() {
|
|
9386
9501
|
this.logger = new BridgeLogger({ prefix: "\u{1F517} Mulink" });
|
|
9387
9502
|
this.schemaParser = new OpenApiSchemaParser();
|
|
9388
9503
|
this.fileManager = new FileSystemManager();
|
|
9389
9504
|
}
|
|
9505
|
+
/**
|
|
9506
|
+
* Generates type-safe API client code from OpenAPI schema
|
|
9507
|
+
*
|
|
9508
|
+
* This is the main entry point for code generation. It validates the configuration,
|
|
9509
|
+
* parses the OpenAPI schema, generates all necessary files, and writes them to disk.
|
|
9510
|
+
*
|
|
9511
|
+
* The generated code follows Next.js 16.0.7 best practices including:
|
|
9512
|
+
* - Server Actions with type safety
|
|
9513
|
+
* - React Query hooks with proper caching
|
|
9514
|
+
* - Middleware support
|
|
9515
|
+
* - Error handling
|
|
9516
|
+
* - Cache tag management
|
|
9517
|
+
*
|
|
9518
|
+
* @param configuration - The bridge configuration containing schema URL, output directory, and framework settings
|
|
9519
|
+
* @returns Promise that resolves when generation is complete
|
|
9520
|
+
* @throws {ValidationError} When configuration is invalid
|
|
9521
|
+
* @throws {SchemaParseError} When OpenAPI schema cannot be parsed
|
|
9522
|
+
* @throws {GenerationError} When code generation fails
|
|
9523
|
+
*
|
|
9524
|
+
* @example
|
|
9525
|
+
* ```typescript
|
|
9526
|
+
* const config: BridgeConfiguration = {
|
|
9527
|
+
* schema: 'https://api.example.com/openapi.json',
|
|
9528
|
+
* outputDir: 'src/generated',
|
|
9529
|
+
* framework: { type: 'nextjs', router: 'app', features: {...} },
|
|
9530
|
+
* api: { baseUrl: 'https://api.example.com', timeout: 30000, retries: 3 }
|
|
9531
|
+
* };
|
|
9532
|
+
* await bridge.generate(config);
|
|
9533
|
+
* ```
|
|
9534
|
+
*/
|
|
9390
9535
|
async generate(configuration) {
|
|
9391
9536
|
const startTime = Date.now();
|
|
9392
9537
|
try {
|
|
9393
9538
|
this.logger.info("Starting code generation...");
|
|
9394
|
-
|
|
9539
|
+
this.validateConfiguration(configuration);
|
|
9395
9540
|
this.logger.info(`Parsing schema: ${configuration.schema}`);
|
|
9396
9541
|
const parsedSchema = await this.schemaParser.parse(configuration.schema);
|
|
9397
9542
|
this.logger.debug(
|
|
@@ -9420,16 +9565,17 @@ var BridgeCore = class {
|
|
|
9420
9565
|
throw error;
|
|
9421
9566
|
}
|
|
9422
9567
|
}
|
|
9423
|
-
|
|
9568
|
+
validateConfiguration(configuration) {
|
|
9424
9569
|
const errors = [];
|
|
9425
|
-
if (!configuration.schema
|
|
9570
|
+
if (!configuration.schema.trim()) {
|
|
9426
9571
|
errors.push("Schema URL is required and cannot be empty");
|
|
9427
9572
|
}
|
|
9428
|
-
if (!configuration.outputDir
|
|
9573
|
+
if (!configuration.outputDir.trim()) {
|
|
9429
9574
|
errors.push("Output directory is required and cannot be empty");
|
|
9430
9575
|
}
|
|
9431
9576
|
if (configuration.framework.type !== "nextjs") {
|
|
9432
|
-
|
|
9577
|
+
const frameworkType = configuration.framework.type;
|
|
9578
|
+
errors.push(`Only Next.js framework is currently supported, got: ${frameworkType}`);
|
|
9433
9579
|
}
|
|
9434
9580
|
if (errors.length > 0) {
|
|
9435
9581
|
throw new ValidationError(
|
|
@@ -9446,8 +9592,10 @@ var BridgeCore = class {
|
|
|
9446
9592
|
case "nextjs":
|
|
9447
9593
|
const nextJsGenerator = new NextJsCodeGenerator(config);
|
|
9448
9594
|
return await nextJsGenerator.generate(context);
|
|
9449
|
-
default:
|
|
9450
|
-
|
|
9595
|
+
default: {
|
|
9596
|
+
const frameworkType = config.framework.type;
|
|
9597
|
+
throw new GenerationError(`Unsupported framework: ${frameworkType}`);
|
|
9598
|
+
}
|
|
9451
9599
|
}
|
|
9452
9600
|
} catch (error) {
|
|
9453
9601
|
if (error instanceof GenerationError) {
|
|
@@ -9481,7 +9629,7 @@ var BridgeCore = class {
|
|
|
9481
9629
|
logGenerationSummary(files) {
|
|
9482
9630
|
const fileTypes = files.reduce(
|
|
9483
9631
|
(acc, file) => {
|
|
9484
|
-
acc[file.type] = (acc[file.type]
|
|
9632
|
+
acc[file.type] = (acc[file.type] ?? 0) + 1;
|
|
9485
9633
|
return acc;
|
|
9486
9634
|
},
|
|
9487
9635
|
{}
|
|
@@ -9500,9 +9648,24 @@ var BridgeCore = class {
|
|
|
9500
9648
|
});
|
|
9501
9649
|
}
|
|
9502
9650
|
}
|
|
9503
|
-
|
|
9651
|
+
/**
|
|
9652
|
+
* Validates an OpenAPI schema without generating code
|
|
9653
|
+
*
|
|
9654
|
+
* Useful for checking schema validity before running full generation.
|
|
9655
|
+
*
|
|
9656
|
+
* @param schemaUrl - URL or file path to the OpenAPI schema
|
|
9657
|
+
* @returns Promise resolving to true if schema is valid, false otherwise
|
|
9658
|
+
*
|
|
9659
|
+
* @example
|
|
9660
|
+
* ```typescript
|
|
9661
|
+
* const isValid = await bridge.validateSchema('https://api.example.com/openapi.json');
|
|
9662
|
+
* if (isValid) {
|
|
9663
|
+
* await bridge.generate(config);
|
|
9664
|
+
* }
|
|
9665
|
+
* ```
|
|
9666
|
+
*/
|
|
9504
9667
|
async validateSchema(schemaUrl) {
|
|
9505
|
-
if (!schemaUrl
|
|
9668
|
+
if (!schemaUrl.trim()) {
|
|
9506
9669
|
this.logger.warn("Schema URL is empty or invalid");
|
|
9507
9670
|
return false;
|
|
9508
9671
|
}
|
|
@@ -9516,8 +9679,25 @@ var BridgeCore = class {
|
|
|
9516
9679
|
return false;
|
|
9517
9680
|
}
|
|
9518
9681
|
}
|
|
9682
|
+
/**
|
|
9683
|
+
* Retrieves metadata from an OpenAPI schema
|
|
9684
|
+
*
|
|
9685
|
+
* Extracts schema metadata including title, version, description, and other
|
|
9686
|
+
* OpenAPI document information without parsing the entire schema.
|
|
9687
|
+
*
|
|
9688
|
+
* @param schemaUrl - URL or file path to the OpenAPI schema
|
|
9689
|
+
* @returns Promise resolving to schema metadata
|
|
9690
|
+
* @throws {BridgeError} When schema URL is invalid
|
|
9691
|
+
* @throws {SchemaParseError} When schema cannot be parsed
|
|
9692
|
+
*
|
|
9693
|
+
* @example
|
|
9694
|
+
* ```typescript
|
|
9695
|
+
* const metadata = await bridge.getSchemaInfo('https://api.example.com/openapi.json');
|
|
9696
|
+
* console.log(`Schema: ${metadata.title} v${metadata.version}`);
|
|
9697
|
+
* ```
|
|
9698
|
+
*/
|
|
9519
9699
|
async getSchemaInfo(schemaUrl) {
|
|
9520
|
-
if (!schemaUrl
|
|
9700
|
+
if (!schemaUrl.trim()) {
|
|
9521
9701
|
throw new BridgeError("Schema URL is required and cannot be empty", "INVALID_SCHEMA_URL");
|
|
9522
9702
|
}
|
|
9523
9703
|
try {
|
|
@@ -9529,6 +9709,18 @@ var BridgeCore = class {
|
|
|
9529
9709
|
throw new SchemaParseError(`Failed to parse schema: ${errorMessage}`, schemaUrl);
|
|
9530
9710
|
}
|
|
9531
9711
|
}
|
|
9712
|
+
/**
|
|
9713
|
+
* Clears the internal schema cache
|
|
9714
|
+
*
|
|
9715
|
+
* Useful when you need to force re-fetching of schemas or during development.
|
|
9716
|
+
* The cache is automatically managed, but this method allows manual invalidation.
|
|
9717
|
+
*
|
|
9718
|
+
* @example
|
|
9719
|
+
* ```typescript
|
|
9720
|
+
* bridge.clearCache();
|
|
9721
|
+
* await bridge.generate(config); // Will fetch fresh schema
|
|
9722
|
+
* ```
|
|
9723
|
+
*/
|
|
9532
9724
|
clearCache() {
|
|
9533
9725
|
try {
|
|
9534
9726
|
this.schemaParser.clearCache();
|
|
@@ -9627,7 +9819,7 @@ var ConfigurationLoader = class {
|
|
|
9627
9819
|
}
|
|
9628
9820
|
async load(configPath) {
|
|
9629
9821
|
if (!configPath?.trim()) {
|
|
9630
|
-
configPath = "
|
|
9822
|
+
configPath = "link.config.json";
|
|
9631
9823
|
}
|
|
9632
9824
|
const resolvedPath = path2.resolve(process.cwd(), configPath);
|
|
9633
9825
|
try {
|
|
@@ -9693,7 +9885,7 @@ ${errorMessages}`,
|
|
|
9693
9885
|
);
|
|
9694
9886
|
}
|
|
9695
9887
|
}
|
|
9696
|
-
|
|
9888
|
+
createDefault(framework = "nextjs") {
|
|
9697
9889
|
const defaultConfig = {
|
|
9698
9890
|
schema: "https://petstore3.swagger.io/api/v3/openapi.json",
|
|
9699
9891
|
outputDir: "src/generated",
|
|
@@ -9738,7 +9930,7 @@ ${errorMessages}`,
|
|
|
9738
9930
|
}
|
|
9739
9931
|
async save(config, configPath) {
|
|
9740
9932
|
if (!configPath?.trim()) {
|
|
9741
|
-
configPath = "
|
|
9933
|
+
configPath = "link.config.json";
|
|
9742
9934
|
}
|
|
9743
9935
|
const resolvedPath = path2.resolve(process.cwd(), configPath);
|
|
9744
9936
|
try {
|
|
@@ -9782,5 +9974,5 @@ ${errorMessages}`,
|
|
|
9782
9974
|
};
|
|
9783
9975
|
|
|
9784
9976
|
export { BridgeCore, BridgeError, BridgeLogger, ConfigurationLoader, FileSystemManager, GenerationError, LogLevel, NextJsCodeGenerator, OpenApiSchemaParser, SchemaParseError, ValidationError, VersionChecker, __name, checkAndNotifyUpdates, createBridgeVersionChecker };
|
|
9785
|
-
//# sourceMappingURL=chunk-
|
|
9786
|
-
//# sourceMappingURL=chunk-
|
|
9977
|
+
//# sourceMappingURL=chunk-2WVV6AOO.js.map
|
|
9978
|
+
//# sourceMappingURL=chunk-2WVV6AOO.js.map
|