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
|
@@ -13,17 +13,48 @@ var __defProp = Object.defineProperty;
|
|
|
13
13
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
14
14
|
|
|
15
15
|
// src/core/types.ts
|
|
16
|
-
var BridgeError = class extends Error {
|
|
16
|
+
var BridgeError = class _BridgeError extends Error {
|
|
17
17
|
static {
|
|
18
18
|
__name(this, "BridgeError");
|
|
19
19
|
}
|
|
20
20
|
code;
|
|
21
21
|
details;
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
timestamp;
|
|
23
|
+
cause;
|
|
24
|
+
constructor(message, code, details, cause) {
|
|
25
|
+
super(message, { cause });
|
|
24
26
|
this.name = "BridgeError";
|
|
25
27
|
this.code = code;
|
|
26
28
|
this.details = details;
|
|
29
|
+
this.timestamp = Date.now();
|
|
30
|
+
this.cause = cause;
|
|
31
|
+
if (Error.captureStackTrace) {
|
|
32
|
+
Error.captureStackTrace(this, _BridgeError);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Converts the error to a JSON-serializable object
|
|
37
|
+
*
|
|
38
|
+
* @returns JSON representation of the error
|
|
39
|
+
*/
|
|
40
|
+
toJSON() {
|
|
41
|
+
return {
|
|
42
|
+
name: this.name,
|
|
43
|
+
message: this.message,
|
|
44
|
+
code: this.code,
|
|
45
|
+
details: this.details,
|
|
46
|
+
timestamp: this.timestamp,
|
|
47
|
+
stack: this.stack,
|
|
48
|
+
cause: this.cause?.message
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Checks if this error is retryable
|
|
53
|
+
*
|
|
54
|
+
* @returns True if the operation that caused this error can be retried
|
|
55
|
+
*/
|
|
56
|
+
isRetryable() {
|
|
57
|
+
return false;
|
|
27
58
|
}
|
|
28
59
|
};
|
|
29
60
|
var ValidationError = class extends BridgeError {
|
|
@@ -31,28 +62,42 @@ var ValidationError = class extends BridgeError {
|
|
|
31
62
|
__name(this, "ValidationError");
|
|
32
63
|
}
|
|
33
64
|
errors;
|
|
34
|
-
constructor(message, errors) {
|
|
35
|
-
super(message, "VALIDATION_ERROR", errors);
|
|
65
|
+
constructor(message, errors, cause) {
|
|
66
|
+
super(message, "VALIDATION_ERROR", errors, cause);
|
|
36
67
|
this.errors = errors;
|
|
37
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Gets formatted validation error messages
|
|
71
|
+
*
|
|
72
|
+
* @returns Array of formatted error messages
|
|
73
|
+
*/
|
|
74
|
+
getFormattedErrors() {
|
|
75
|
+
return this.errors.errors.map((err) => {
|
|
76
|
+
const path3 = err.path.join(".");
|
|
77
|
+
return `${path3}: ${err.message}`;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
38
80
|
};
|
|
39
81
|
var SchemaParseError = class extends BridgeError {
|
|
40
82
|
static {
|
|
41
83
|
__name(this, "SchemaParseError");
|
|
42
84
|
}
|
|
43
85
|
schemaUrl;
|
|
44
|
-
constructor(message, schemaUrl) {
|
|
45
|
-
super(message, "SCHEMA_PARSE_ERROR", { schemaUrl });
|
|
86
|
+
constructor(message, schemaUrl, cause) {
|
|
87
|
+
super(message, "SCHEMA_PARSE_ERROR", { schemaUrl }, cause);
|
|
46
88
|
this.schemaUrl = schemaUrl;
|
|
47
89
|
}
|
|
90
|
+
isRetryable() {
|
|
91
|
+
return this.cause instanceof TypeError && this.cause.message.includes("fetch");
|
|
92
|
+
}
|
|
48
93
|
};
|
|
49
94
|
var GenerationError = class extends BridgeError {
|
|
50
95
|
static {
|
|
51
96
|
__name(this, "GenerationError");
|
|
52
97
|
}
|
|
53
98
|
file;
|
|
54
|
-
constructor(message, file) {
|
|
55
|
-
super(message, "GENERATION_ERROR", { file });
|
|
99
|
+
constructor(message, file, cause) {
|
|
100
|
+
super(message, "GENERATION_ERROR", { file }, cause);
|
|
56
101
|
this.file = file;
|
|
57
102
|
}
|
|
58
103
|
};
|
|
@@ -140,8 +185,8 @@ var BridgeLogger = class {
|
|
|
140
185
|
if (!this.options.colors || !process.stdout.isTTY) {
|
|
141
186
|
console.log(message);
|
|
142
187
|
return {
|
|
143
|
-
succeed: /* @__PURE__ */ __name((msg) => console.log(colorize(`\u2713 ${msg
|
|
144
|
-
fail: /* @__PURE__ */ __name((msg) => console.error(colorize(`\u2717 ${msg
|
|
188
|
+
succeed: /* @__PURE__ */ __name((msg) => console.log(colorize(`\u2713 ${msg ?? message}`, "green")), "succeed"),
|
|
189
|
+
fail: /* @__PURE__ */ __name((msg) => console.error(colorize(`\u2717 ${msg ?? message}`, "red")), "fail"),
|
|
145
190
|
stop: /* @__PURE__ */ __name(() => {
|
|
146
191
|
}, "stop")
|
|
147
192
|
};
|
|
@@ -149,17 +194,20 @@ var BridgeLogger = class {
|
|
|
149
194
|
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
150
195
|
let i = 0;
|
|
151
196
|
const interval = setInterval(() => {
|
|
152
|
-
|
|
197
|
+
const frame = frames[i++ % frames.length];
|
|
198
|
+
if (frame) {
|
|
199
|
+
process.stdout.write(`\r${colorize(frame, "cyan")} ${message}`);
|
|
200
|
+
}
|
|
153
201
|
}, 100);
|
|
154
202
|
return {
|
|
155
203
|
succeed: /* @__PURE__ */ __name((msg) => {
|
|
156
204
|
clearInterval(interval);
|
|
157
|
-
process.stdout.write(`\r${colorize("\u2713", "green")} ${msg
|
|
205
|
+
process.stdout.write(`\r${colorize("\u2713", "green")} ${msg ?? message}
|
|
158
206
|
`);
|
|
159
207
|
}, "succeed"),
|
|
160
208
|
fail: /* @__PURE__ */ __name((msg) => {
|
|
161
209
|
clearInterval(interval);
|
|
162
|
-
process.stdout.write(`\r${colorize("\u2717", "red")} ${msg
|
|
210
|
+
process.stdout.write(`\r${colorize("\u2717", "red")} ${msg ?? message}
|
|
163
211
|
`);
|
|
164
212
|
}, "fail"),
|
|
165
213
|
stop: /* @__PURE__ */ __name(() => {
|
|
@@ -182,7 +230,7 @@ var VersionChecker = class {
|
|
|
182
230
|
constructor(packageName, currentVersion, logger) {
|
|
183
231
|
this.packageName = packageName;
|
|
184
232
|
this.currentVersion = currentVersion;
|
|
185
|
-
this.logger = logger
|
|
233
|
+
this.logger = logger ?? new BridgeLogger({ prefix: "\u{1F50D} Version Check" });
|
|
186
234
|
}
|
|
187
235
|
/**
|
|
188
236
|
* Check if a new version is available
|
|
@@ -237,7 +285,7 @@ var VersionChecker = class {
|
|
|
237
285
|
/**
|
|
238
286
|
* Get the appropriate upgrade command for the detected package manager
|
|
239
287
|
*/
|
|
240
|
-
getUpgradeCommand(
|
|
288
|
+
getUpgradeCommand(_versionInfo) {
|
|
241
289
|
const packageManager = this.detectPackageManager();
|
|
242
290
|
switch (packageManager) {
|
|
243
291
|
case "yarn":
|
|
@@ -311,8 +359,8 @@ var VersionChecker = class {
|
|
|
311
359
|
const currentParts = this.parseVersion(current);
|
|
312
360
|
const latestParts = this.parseVersion(latest);
|
|
313
361
|
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
|
|
314
|
-
const currentPart = currentParts[i]
|
|
315
|
-
const latestPart = latestParts[i]
|
|
362
|
+
const currentPart = currentParts[i] ?? 0;
|
|
363
|
+
const latestPart = latestParts[i] ?? 0;
|
|
316
364
|
if (latestPart > currentPart) {
|
|
317
365
|
return true;
|
|
318
366
|
} else if (latestPart < currentPart) {
|
|
@@ -386,7 +434,7 @@ function createBridgeVersionChecker(logger) {
|
|
|
386
434
|
if (fs.existsSync(pkgPath)) {
|
|
387
435
|
const pkgRaw = fs.readFileSync(pkgPath, { encoding: "utf-8" });
|
|
388
436
|
const pkg = JSON.parse(pkgRaw);
|
|
389
|
-
if (pkg
|
|
437
|
+
if (pkg.version) {
|
|
390
438
|
currentVersion = pkg.version;
|
|
391
439
|
}
|
|
392
440
|
} else {
|
|
@@ -394,13 +442,13 @@ function createBridgeVersionChecker(logger) {
|
|
|
394
442
|
if (fs.existsSync(cwdPkg)) {
|
|
395
443
|
const pkgRaw = fs.readFileSync(cwdPkg, { encoding: "utf-8" });
|
|
396
444
|
const pkg = JSON.parse(pkgRaw);
|
|
397
|
-
if (pkg
|
|
445
|
+
if (pkg.version) {
|
|
398
446
|
currentVersion = pkg.version;
|
|
399
447
|
}
|
|
400
448
|
}
|
|
401
449
|
}
|
|
402
450
|
} catch (err) {
|
|
403
|
-
const l = logger
|
|
451
|
+
const l = logger ?? new BridgeLogger({ prefix: "\u{1F50D} Version Check" });
|
|
404
452
|
l.debug("Failed to read package.json for current version, falling back to default", err);
|
|
405
453
|
}
|
|
406
454
|
return new VersionChecker(packageName, currentVersion, logger);
|
|
@@ -438,7 +486,7 @@ var OpenApiSchemaParser = class {
|
|
|
438
486
|
async fetchSchema(schemaUrl) {
|
|
439
487
|
const versionChecker = createBridgeVersionChecker();
|
|
440
488
|
const currentVersion = versionChecker.getCurrentVersion();
|
|
441
|
-
if (!schemaUrl
|
|
489
|
+
if (!schemaUrl.trim()) {
|
|
442
490
|
throw new SchemaParseError("Schema URL is required and cannot be empty", schemaUrl);
|
|
443
491
|
}
|
|
444
492
|
if (schemaUrl.startsWith("http")) {
|
|
@@ -459,7 +507,7 @@ var OpenApiSchemaParser = class {
|
|
|
459
507
|
schemaUrl
|
|
460
508
|
);
|
|
461
509
|
}
|
|
462
|
-
const contentType = response.headers.get("content-type")
|
|
510
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
463
511
|
if (!contentType.includes("application/json") && !contentType.includes("application/yaml") && !contentType.includes("text/yaml")) {
|
|
464
512
|
throw new SchemaParseError(
|
|
465
513
|
`Unsupported content type: ${contentType}. Expected JSON or YAML.`,
|
|
@@ -489,10 +537,10 @@ var OpenApiSchemaParser = class {
|
|
|
489
537
|
);
|
|
490
538
|
}
|
|
491
539
|
}
|
|
492
|
-
|
|
540
|
+
parseDocument(document) {
|
|
493
541
|
const version = this.detectVersion(document);
|
|
494
542
|
const normalizedDocument = this.normalizeDocument(document, version);
|
|
495
|
-
return {
|
|
543
|
+
return Promise.resolve({
|
|
496
544
|
version,
|
|
497
545
|
document: normalizedDocument,
|
|
498
546
|
openApiSpec: normalizedDocument,
|
|
@@ -501,7 +549,7 @@ var OpenApiSchemaParser = class {
|
|
|
501
549
|
security: this.extractSecurity(normalizedDocument),
|
|
502
550
|
servers: this.extractServers(normalizedDocument, document),
|
|
503
551
|
metadata: this.extractMetadata(normalizedDocument)
|
|
504
|
-
};
|
|
552
|
+
});
|
|
505
553
|
}
|
|
506
554
|
detectVersion(document) {
|
|
507
555
|
if (!document || typeof document !== "object") {
|
|
@@ -536,7 +584,7 @@ var OpenApiSchemaParser = class {
|
|
|
536
584
|
info: v2Doc.info,
|
|
537
585
|
servers: v2Doc.host ? [
|
|
538
586
|
{
|
|
539
|
-
url: `${v2Doc.schemes?.[0]
|
|
587
|
+
url: `${v2Doc.schemes?.[0] ?? "https"}://${v2Doc.host}${v2Doc.basePath ?? ""}`
|
|
540
588
|
}
|
|
541
589
|
] : [],
|
|
542
590
|
paths: {},
|
|
@@ -681,20 +729,20 @@ var OpenApiSchemaParser = class {
|
|
|
681
729
|
flows: {
|
|
682
730
|
implicit: v2Scheme.flow === "implicit" ? {
|
|
683
731
|
authorizationUrl: v2Scheme.authorizationUrl,
|
|
684
|
-
scopes: v2Scheme.scopes
|
|
732
|
+
scopes: v2Scheme.scopes ?? {}
|
|
685
733
|
} : void 0,
|
|
686
734
|
password: v2Scheme.flow === "password" ? {
|
|
687
735
|
tokenUrl: v2Scheme.tokenUrl,
|
|
688
|
-
scopes: v2Scheme.scopes
|
|
736
|
+
scopes: v2Scheme.scopes ?? {}
|
|
689
737
|
} : void 0,
|
|
690
738
|
clientCredentials: v2Scheme.flow === "application" ? {
|
|
691
739
|
tokenUrl: v2Scheme.tokenUrl,
|
|
692
|
-
scopes: v2Scheme.scopes
|
|
740
|
+
scopes: v2Scheme.scopes ?? {}
|
|
693
741
|
} : void 0,
|
|
694
742
|
authorizationCode: v2Scheme.flow === "accessCode" ? {
|
|
695
743
|
authorizationUrl: v2Scheme.authorizationUrl,
|
|
696
744
|
tokenUrl: v2Scheme.tokenUrl,
|
|
697
|
-
scopes: v2Scheme.scopes
|
|
745
|
+
scopes: v2Scheme.scopes ?? {}
|
|
698
746
|
} : void 0
|
|
699
747
|
}
|
|
700
748
|
};
|
|
@@ -720,18 +768,18 @@ var OpenApiSchemaParser = class {
|
|
|
720
768
|
const operation = pathItem[method.toLowerCase()];
|
|
721
769
|
if (!operation) continue;
|
|
722
770
|
const endpoint = {
|
|
723
|
-
id: operation.operationId
|
|
771
|
+
id: operation.operationId ?? `${method.toLowerCase()}_${path3.replace(/[^a-zA-Z0-9]/g, "_")}`,
|
|
724
772
|
path: path3,
|
|
725
773
|
method,
|
|
726
774
|
operationId: operation.operationId,
|
|
727
775
|
summary: operation.summary,
|
|
728
776
|
description: operation.description,
|
|
729
|
-
tags: operation.tags
|
|
777
|
+
tags: operation.tags ?? [],
|
|
730
778
|
parameters: this.extractParameters(operation.parameters, pathItem.parameters, document),
|
|
731
779
|
requestBody: this.extractRequestBody(operation.requestBody, document),
|
|
732
780
|
responses: this.extractResponses(operation.responses, document),
|
|
733
|
-
security: operation.security
|
|
734
|
-
deprecated: operation.deprecated
|
|
781
|
+
security: operation.security ?? [],
|
|
782
|
+
deprecated: operation.deprecated ?? false,
|
|
735
783
|
metadata: this.extractEndpointMetadata(operation)
|
|
736
784
|
};
|
|
737
785
|
endpoints.push(endpoint);
|
|
@@ -741,7 +789,7 @@ var OpenApiSchemaParser = class {
|
|
|
741
789
|
}
|
|
742
790
|
extractParameters(operationParams, pathParams, document) {
|
|
743
791
|
const parameters = [];
|
|
744
|
-
const allParams = [...pathParams
|
|
792
|
+
const allParams = [...pathParams ?? [], ...operationParams ?? []];
|
|
745
793
|
for (const param of allParams) {
|
|
746
794
|
if (!param) continue;
|
|
747
795
|
if ("$ref" in param) {
|
|
@@ -751,11 +799,11 @@ var OpenApiSchemaParser = class {
|
|
|
751
799
|
const parameter = {
|
|
752
800
|
name: param.name,
|
|
753
801
|
in: param.in,
|
|
754
|
-
required: param.required
|
|
802
|
+
required: param.required ?? param.in === "path",
|
|
755
803
|
schema: this.convertSchemaToZod(param.schema, document),
|
|
756
804
|
description: param.description,
|
|
757
805
|
example: param.example,
|
|
758
|
-
deprecated: param.deprecated
|
|
806
|
+
deprecated: param.deprecated ?? false
|
|
759
807
|
};
|
|
760
808
|
parameters.push(parameter);
|
|
761
809
|
}
|
|
@@ -774,7 +822,7 @@ var OpenApiSchemaParser = class {
|
|
|
774
822
|
}
|
|
775
823
|
}
|
|
776
824
|
return {
|
|
777
|
-
required: requestBody.required
|
|
825
|
+
required: requestBody.required ?? false,
|
|
778
826
|
description: requestBody.description,
|
|
779
827
|
content
|
|
780
828
|
};
|
|
@@ -814,7 +862,7 @@ var OpenApiSchemaParser = class {
|
|
|
814
862
|
schema: this.convertSchemaToZod(schema, document),
|
|
815
863
|
description: schema.description,
|
|
816
864
|
example: schema.example,
|
|
817
|
-
deprecated: schema.deprecated
|
|
865
|
+
deprecated: schema.deprecated ?? false
|
|
818
866
|
});
|
|
819
867
|
}
|
|
820
868
|
}
|
|
@@ -848,7 +896,7 @@ var OpenApiSchemaParser = class {
|
|
|
848
896
|
const v2Doc = originalDoc;
|
|
849
897
|
return [
|
|
850
898
|
{
|
|
851
|
-
url: `${v2Doc.schemes?.[0]
|
|
899
|
+
url: `${v2Doc.schemes?.[0] ?? "https"}://${v2Doc.host}${v2Doc.basePath ?? ""}`,
|
|
852
900
|
description: "Converted from Swagger 2.0"
|
|
853
901
|
}
|
|
854
902
|
];
|
|
@@ -868,9 +916,9 @@ var OpenApiSchemaParser = class {
|
|
|
868
916
|
}
|
|
869
917
|
extractEndpointMetadata(operation) {
|
|
870
918
|
return {
|
|
871
|
-
requiresAuth: operation.security
|
|
919
|
+
requiresAuth: (operation.security?.length ?? 0) > 0,
|
|
872
920
|
cacheStrategy: "default",
|
|
873
|
-
revalidationTags: operation.tags
|
|
921
|
+
revalidationTags: operation.tags ?? [],
|
|
874
922
|
streaming: false,
|
|
875
923
|
fileUpload: this.hasFileUpload(operation)
|
|
876
924
|
};
|
|
@@ -878,7 +926,7 @@ var OpenApiSchemaParser = class {
|
|
|
878
926
|
hasFileUpload(operation) {
|
|
879
927
|
if (!operation.requestBody || "$ref" in operation.requestBody) return false;
|
|
880
928
|
const content = operation.requestBody.content;
|
|
881
|
-
return Object.keys(content
|
|
929
|
+
return Object.keys(content ?? {}).some(
|
|
882
930
|
(mediaType) => mediaType.includes("multipart/form-data") || mediaType.includes("application/octet-stream")
|
|
883
931
|
);
|
|
884
932
|
}
|
|
@@ -966,7 +1014,7 @@ var OpenApiSchemaParser = class {
|
|
|
966
1014
|
case "object":
|
|
967
1015
|
if (schema.properties) {
|
|
968
1016
|
const shape = {};
|
|
969
|
-
const required = schema.required
|
|
1017
|
+
const required = schema.required ?? [];
|
|
970
1018
|
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
971
1019
|
let propZodSchema = this.convertSchemaToZod(propSchema, document);
|
|
972
1020
|
if (!required.includes(propName)) {
|
|
@@ -998,7 +1046,7 @@ var ApiClientGenerator = class {
|
|
|
998
1046
|
* Generate import paths based on configuration
|
|
999
1047
|
*/
|
|
1000
1048
|
buildImportPath(relativePath) {
|
|
1001
|
-
const outputDirectory = this.configuration.outputDir
|
|
1049
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
1002
1050
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
1003
1051
|
let importBasePath = outputDirectory;
|
|
1004
1052
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -1006,24 +1054,24 @@ var ApiClientGenerator = class {
|
|
|
1006
1054
|
}
|
|
1007
1055
|
return `@/${importBasePath}/${cleanPath}`;
|
|
1008
1056
|
}
|
|
1009
|
-
|
|
1057
|
+
generate(context) {
|
|
1010
1058
|
const { schema } = context;
|
|
1011
1059
|
const generatedFiles = [];
|
|
1012
1060
|
generatedFiles.push(this.generateEnhancedBaseClient());
|
|
1013
1061
|
generatedFiles.push(...this.generateEndpointClients(schema.endpoints));
|
|
1014
1062
|
generatedFiles.push(this.generateMainClient(schema.endpoints));
|
|
1015
1063
|
generatedFiles.push(this.generateClientUtilities());
|
|
1016
|
-
return generatedFiles;
|
|
1064
|
+
return Promise.resolve(generatedFiles);
|
|
1017
1065
|
}
|
|
1018
1066
|
generateEnhancedBaseClient() {
|
|
1019
1067
|
const { api, auth } = this.configuration;
|
|
1020
1068
|
const authUtilsImport = this.buildImportPath("auth/utils");
|
|
1021
|
-
const authPath = auth?.authPath
|
|
1022
|
-
const tokenGetter = auth?.tokenGetter
|
|
1069
|
+
const authPath = auth?.authPath ?? "@/lib/auth";
|
|
1070
|
+
const tokenGetter = auth?.tokenGetter ?? "auth";
|
|
1023
1071
|
const content = `/**
|
|
1024
1072
|
* The HTTP client is automatically created by "mulink"
|
|
1025
1073
|
*
|
|
1026
|
-
* Next.js 16.0.
|
|
1074
|
+
* Next.js 16.0.7 Best Practices:
|
|
1027
1075
|
* - Proper separation of client/server code
|
|
1028
1076
|
* - Dynamic imports for server-only modules
|
|
1029
1077
|
* - Works in both Server Components and Client Components
|
|
@@ -1034,7 +1082,7 @@ import { cache } from 'react'
|
|
|
1034
1082
|
|
|
1035
1083
|
/**
|
|
1036
1084
|
* Server-only modules interface
|
|
1037
|
-
* Next.js 16.0.
|
|
1085
|
+
* Next.js 16.0.7: These modules are only available on the server
|
|
1038
1086
|
* We use dynamic imports to avoid bundling them in the client
|
|
1039
1087
|
*/
|
|
1040
1088
|
type NextHeadersModule = typeof import('next/headers')
|
|
@@ -1058,7 +1106,7 @@ function toMutableHeaders(source: NextReadonlyHeaders) {
|
|
|
1058
1106
|
|
|
1059
1107
|
/**
|
|
1060
1108
|
* Lazy load server-only modules only when needed (server-side)
|
|
1061
|
-
* Next.js 16.0.
|
|
1109
|
+
* Next.js 16.0.7: This ensures server-only code is not bundled in the client
|
|
1062
1110
|
*
|
|
1063
1111
|
* @returns Server-only modules or undefined if on client-side
|
|
1064
1112
|
*/
|
|
@@ -1066,7 +1114,7 @@ async function getServerModules() {
|
|
|
1066
1114
|
if (serverOnlyModules !== null) return serverOnlyModules
|
|
1067
1115
|
|
|
1068
1116
|
// Only attempt to import on server-side
|
|
1069
|
-
// Next.js 16.0.
|
|
1117
|
+
// Next.js 16.0.7: typeof window check ensures we're on the server
|
|
1070
1118
|
if (typeof window === 'undefined') {
|
|
1071
1119
|
try {
|
|
1072
1120
|
const headersModule = await import('next/headers').catch(() => null)
|
|
@@ -1292,11 +1340,11 @@ export class BaseApiClient {
|
|
|
1292
1340
|
private readonly middleware: RequestMiddleware[] = []
|
|
1293
1341
|
|
|
1294
1342
|
constructor() {
|
|
1295
|
-
this.baseUrl = process.env.NEXT_PUBLIC_API_URL || process.env.API_BASE_URL || '${api
|
|
1296
|
-
this.defaultTimeout = ${api
|
|
1297
|
-
this.defaultRetries = ${api
|
|
1298
|
-
this.defaultHeaders = ${JSON.stringify(api
|
|
1299
|
-
this.defaultUserAgent = '${api
|
|
1343
|
+
this.baseUrl = process.env.NEXT_PUBLIC_API_URL || process.env.API_BASE_URL || '${api.baseUrl || "http://localhost:3000/api"}'
|
|
1344
|
+
this.defaultTimeout = ${api.timeout || 3e4}
|
|
1345
|
+
this.defaultRetries = ${api.retries || 3}
|
|
1346
|
+
this.defaultHeaders = ${JSON.stringify(api.headers || {}, null, 6)}
|
|
1347
|
+
this.defaultUserAgent = '${api.userAgent || "Mulink-Client/3.4.5"}'
|
|
1300
1348
|
|
|
1301
1349
|
// Add default middleware
|
|
1302
1350
|
this.addMiddleware({
|
|
@@ -1470,16 +1518,26 @@ export class BaseApiClient {
|
|
|
1470
1518
|
}
|
|
1471
1519
|
|
|
1472
1520
|
// Add query parameters with proper encoding
|
|
1521
|
+
// Filter out undefined, null, empty strings, and empty objects to prevent 422 errors
|
|
1473
1522
|
const searchParams = new URLSearchParams()
|
|
1474
1523
|
for (const [key, value] of Object.entries(queryParameters)) {
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1524
|
+
// Skip undefined, null, and empty strings
|
|
1525
|
+
if (value === undefined || value === null || value === '') {
|
|
1526
|
+
continue
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
// Skip empty objects (like {})
|
|
1530
|
+
if (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) {
|
|
1531
|
+
continue
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
if (Array.isArray(value)) {
|
|
1535
|
+
value.forEach(v => searchParams.append(key, String(v)))
|
|
1536
|
+
} else if (typeof value === 'object') {
|
|
1537
|
+
// Only serialize non-empty objects
|
|
1538
|
+
searchParams.append(key, JSON.stringify(value))
|
|
1539
|
+
} else {
|
|
1540
|
+
searchParams.append(key, String(value))
|
|
1483
1541
|
}
|
|
1484
1542
|
}
|
|
1485
1543
|
|
|
@@ -1652,7 +1710,7 @@ export class BaseApiClient {
|
|
|
1652
1710
|
controller.abort()
|
|
1653
1711
|
}, timeout)
|
|
1654
1712
|
|
|
1655
|
-
// Next.js 16.0.
|
|
1713
|
+
// Next.js 16.0.7: Build fetch options with cache tags and connection
|
|
1656
1714
|
// Only include next options if we're on the server (Next.js App Router)
|
|
1657
1715
|
const fetchInit: RequestInit & { next?: { tags?: string[]; revalidate?: number | false; connection?: string } } = {
|
|
1658
1716
|
...requestConfig,
|
|
@@ -1660,7 +1718,7 @@ export class BaseApiClient {
|
|
|
1660
1718
|
}
|
|
1661
1719
|
|
|
1662
1720
|
// Add Next.js-specific options only if we have cache tags, revalidate, or connection
|
|
1663
|
-
// Next.js 16.0.
|
|
1721
|
+
// Next.js 16.0.7: Enhanced cache tag support with updateTag
|
|
1664
1722
|
if (cacheTags && cacheTags.length > 0 || revalidate !== undefined || connection) {
|
|
1665
1723
|
fetchInit.next = {}
|
|
1666
1724
|
|
|
@@ -1672,7 +1730,7 @@ export class BaseApiClient {
|
|
|
1672
1730
|
fetchInit.next.revalidate = revalidate === false ? false : revalidate
|
|
1673
1731
|
}
|
|
1674
1732
|
|
|
1675
|
-
// Next.js 16.0.
|
|
1733
|
+
// Next.js 16.0.7: Connection keep-alive for persistent connections
|
|
1676
1734
|
if (connection) {
|
|
1677
1735
|
fetchInit.next.connection = connection
|
|
1678
1736
|
}
|
|
@@ -1680,7 +1738,7 @@ export class BaseApiClient {
|
|
|
1680
1738
|
|
|
1681
1739
|
const response = await fetch(url, fetchInit)
|
|
1682
1740
|
|
|
1683
|
-
// Next.js 16.0.
|
|
1741
|
+
// Next.js 16.0.7: Update cache tags dynamically using updateTag from next/cache
|
|
1684
1742
|
// This allows for granular cache invalidation
|
|
1685
1743
|
if (cacheTags && cacheTags.length > 0) {
|
|
1686
1744
|
try {
|
|
@@ -1944,7 +2002,7 @@ async get<TData>(
|
|
|
1944
2002
|
}
|
|
1945
2003
|
generateClientUtilities() {
|
|
1946
2004
|
const { api } = this.configuration;
|
|
1947
|
-
const errorHandling = api
|
|
2005
|
+
const errorHandling = api.errorHandling;
|
|
1948
2006
|
const enableAuthErrorHandling = errorHandling?.enableAuthErrorHandling !== false;
|
|
1949
2007
|
const authErrorHandlerPath = errorHandling?.authErrorHandlerPath || "@/lib/auth-error-handler";
|
|
1950
2008
|
const authErrorMiddlewareCode = enableAuthErrorHandling ? `
|
|
@@ -2341,7 +2399,7 @@ ${clientMethods}
|
|
|
2341
2399
|
const requestOptionsString = requestOptions.join("\n");
|
|
2342
2400
|
const methodSignature = parameterTypes.length > 1 ? `options: {
|
|
2343
2401
|
${parameterTypes.join("\n ")}
|
|
2344
|
-
}` : parameterTypes.length === 1 && parameterTypes[0].includes("config?") ? "options?: { config?: RequestConfiguration }" : `options: { ${parameterTypes.join(", ")} }`;
|
|
2402
|
+
}` : parameterTypes.length === 1 && parameterTypes[0] && parameterTypes[0].includes("config?") ? "options?: { config?: RequestConfiguration }" : `options: { ${parameterTypes.join(", ")} }`;
|
|
2345
2403
|
if (isQuery) {
|
|
2346
2404
|
return `${documentation}
|
|
2347
2405
|
${methodName} = cache(async (${methodSignature}) => {
|
|
@@ -2612,7 +2670,7 @@ var SchemaGenerator = class {
|
|
|
2612
2670
|
visiting.add(schemaName);
|
|
2613
2671
|
const schema = schemaMap.get(schemaName);
|
|
2614
2672
|
if (schema) {
|
|
2615
|
-
const dependencies = dependencyGraph.get(schemaName)
|
|
2673
|
+
const dependencies = dependencyGraph.get(schemaName) ?? /* @__PURE__ */ new Set();
|
|
2616
2674
|
for (const dep of dependencies) {
|
|
2617
2675
|
if (schemaMap.has(dep)) {
|
|
2618
2676
|
visit(dep);
|
|
@@ -2645,13 +2703,13 @@ var SchemaGenerator = class {
|
|
|
2645
2703
|
findSchemaDependencies(schema) {
|
|
2646
2704
|
const dependencies = [];
|
|
2647
2705
|
const schemaObj = schema.schema;
|
|
2648
|
-
if (schemaObj
|
|
2706
|
+
if (schemaObj?._def) {
|
|
2649
2707
|
this.extractDependenciesFromZodSchema(schemaObj, dependencies);
|
|
2650
2708
|
}
|
|
2651
2709
|
return dependencies;
|
|
2652
2710
|
}
|
|
2653
2711
|
extractDependenciesFromZodSchema(zodSchema, dependencies, visited = /* @__PURE__ */ new Set()) {
|
|
2654
|
-
if (!zodSchema
|
|
2712
|
+
if (!zodSchema?._def) return;
|
|
2655
2713
|
const def = zodSchema._def;
|
|
2656
2714
|
if (def._schemaRef) {
|
|
2657
2715
|
const refName = def._schemaRef;
|
|
@@ -2674,7 +2732,7 @@ var SchemaGenerator = class {
|
|
|
2674
2732
|
case "ZodObject":
|
|
2675
2733
|
if (def.shape) {
|
|
2676
2734
|
const shape = def.shape();
|
|
2677
|
-
for (const [
|
|
2735
|
+
for (const [_key, value] of Object.entries(shape)) {
|
|
2678
2736
|
this.extractDependenciesFromZodSchema(value, dependencies, visited);
|
|
2679
2737
|
}
|
|
2680
2738
|
}
|
|
@@ -2706,7 +2764,7 @@ var SchemaGenerator = class {
|
|
|
2706
2764
|
break;
|
|
2707
2765
|
}
|
|
2708
2766
|
}
|
|
2709
|
-
|
|
2767
|
+
generateSchemasFile(context) {
|
|
2710
2768
|
const { schemas, endpoints } = context.schema;
|
|
2711
2769
|
const imports = ['import { z } from "zod"'];
|
|
2712
2770
|
const schemaExports = [];
|
|
@@ -2740,7 +2798,7 @@ var SchemaGenerator = class {
|
|
|
2740
2798
|
|
|
2741
2799
|
${content}`;
|
|
2742
2800
|
}
|
|
2743
|
-
return {
|
|
2801
|
+
return Promise.resolve({
|
|
2744
2802
|
path: "schemas/index.ts",
|
|
2745
2803
|
content,
|
|
2746
2804
|
type: "typescript",
|
|
@@ -2749,7 +2807,7 @@ ${content}`;
|
|
|
2749
2807
|
imports: ["zod"],
|
|
2750
2808
|
dependencies: []
|
|
2751
2809
|
}
|
|
2752
|
-
};
|
|
2810
|
+
});
|
|
2753
2811
|
}
|
|
2754
2812
|
generateEndpointSchemas(endpoint) {
|
|
2755
2813
|
const definitions = [];
|
|
@@ -2784,6 +2842,9 @@ ${content}`;
|
|
|
2784
2842
|
}
|
|
2785
2843
|
const exportName = `${operationName}RequestSchema`;
|
|
2786
2844
|
const primaryContent = endpoint.requestBody.content[0];
|
|
2845
|
+
if (!primaryContent) {
|
|
2846
|
+
throw new Error(`No content found for request body in ${endpoint.method} ${endpoint.path}`);
|
|
2847
|
+
}
|
|
2787
2848
|
const zodSchemaString = this.zodSchemaToString(primaryContent.schema);
|
|
2788
2849
|
const definition = [
|
|
2789
2850
|
`/**`,
|
|
@@ -2807,17 +2868,22 @@ ${content}`;
|
|
|
2807
2868
|
);
|
|
2808
2869
|
if (successResponses.length > 0) {
|
|
2809
2870
|
const successResponse = successResponses[0];
|
|
2871
|
+
if (!successResponse) {
|
|
2872
|
+
return { definitions, exports };
|
|
2873
|
+
}
|
|
2810
2874
|
let zodSchemaString = "z.void()";
|
|
2811
2875
|
if (successResponse.content && successResponse.content.length > 0) {
|
|
2812
2876
|
const primaryContent = successResponse.content[0];
|
|
2813
|
-
|
|
2877
|
+
if (primaryContent) {
|
|
2878
|
+
zodSchemaString = this.zodSchemaToString(primaryContent.schema);
|
|
2879
|
+
}
|
|
2814
2880
|
}
|
|
2815
2881
|
const exportName = `${operationName}ResponseSchema`;
|
|
2816
2882
|
const definition = [
|
|
2817
2883
|
`/**`,
|
|
2818
2884
|
` * Success response schema for ${endpoint.method} ${endpoint.path}`,
|
|
2819
2885
|
` * Status: ${successResponse.statusCode}`,
|
|
2820
|
-
` * ${successResponse.description}`,
|
|
2886
|
+
` * ${successResponse.description ?? ""}`,
|
|
2821
2887
|
` */`,
|
|
2822
2888
|
`export const ${exportName} = ${zodSchemaString}`,
|
|
2823
2889
|
"",
|
|
@@ -2828,15 +2894,18 @@ ${content}`;
|
|
|
2828
2894
|
}
|
|
2829
2895
|
if (errorResponses.length > 0) {
|
|
2830
2896
|
const errorResponse = errorResponses[0];
|
|
2831
|
-
if (errorResponse
|
|
2897
|
+
if (errorResponse?.content && errorResponse.content.length > 0) {
|
|
2832
2898
|
const exportName = `${operationName}ErrorSchema`;
|
|
2833
2899
|
const primaryContent = errorResponse.content[0];
|
|
2900
|
+
if (!primaryContent) {
|
|
2901
|
+
return { definitions, exports };
|
|
2902
|
+
}
|
|
2834
2903
|
const zodSchemaString = this.zodSchemaToString(primaryContent.schema);
|
|
2835
2904
|
const definition = [
|
|
2836
2905
|
`/**`,
|
|
2837
2906
|
` * Error response schema for ${endpoint.method} ${endpoint.path}`,
|
|
2838
2907
|
` * Status: ${errorResponse.statusCode}`,
|
|
2839
|
-
` * ${errorResponse.description}`,
|
|
2908
|
+
` * ${errorResponse.description ?? ""}`,
|
|
2840
2909
|
` */`,
|
|
2841
2910
|
`export const ${exportName} = ${zodSchemaString}`,
|
|
2842
2911
|
"",
|
|
@@ -2927,8 +2996,8 @@ ${schemaProperties.join(",\n")}
|
|
|
2927
2996
|
};
|
|
2928
2997
|
}
|
|
2929
2998
|
zodSchemaToString(schema, context) {
|
|
2930
|
-
if (!schema
|
|
2931
|
-
if (schema
|
|
2999
|
+
if (!schema?._def) {
|
|
3000
|
+
if (schema?._schemaRef) {
|
|
2932
3001
|
const refName = schema._schemaRef;
|
|
2933
3002
|
const registeredSchema = this.schemaRegistry.get(refName);
|
|
2934
3003
|
if (registeredSchema) {
|
|
@@ -3096,15 +3165,21 @@ export const errorMessages = {
|
|
|
3096
3165
|
const assignments = line.match(/(\w+)\s*[:=]/g);
|
|
3097
3166
|
if (assignments) {
|
|
3098
3167
|
assignments.forEach((match) => {
|
|
3099
|
-
const
|
|
3100
|
-
|
|
3168
|
+
const parts = match.split(/[:=]/);
|
|
3169
|
+
if (parts[0]) {
|
|
3170
|
+
const identifier = parts[0].trim();
|
|
3171
|
+
usedIdentifiers.add(identifier);
|
|
3172
|
+
}
|
|
3101
3173
|
});
|
|
3102
3174
|
}
|
|
3103
3175
|
const functionCalls = line.match(/(\w+)\s*\(/g);
|
|
3104
3176
|
if (functionCalls) {
|
|
3105
3177
|
functionCalls.forEach((match) => {
|
|
3106
|
-
const
|
|
3107
|
-
|
|
3178
|
+
const parts = match.split("(");
|
|
3179
|
+
if (parts[0]) {
|
|
3180
|
+
const identifier = parts[0].trim();
|
|
3181
|
+
usedIdentifiers.add(identifier);
|
|
3182
|
+
}
|
|
3108
3183
|
});
|
|
3109
3184
|
}
|
|
3110
3185
|
if (line.includes("z.infer")) {
|
|
@@ -3122,10 +3197,10 @@ export const errorMessages = {
|
|
|
3122
3197
|
continue;
|
|
3123
3198
|
}
|
|
3124
3199
|
const importMatch = line.match(/import\s*\{([^}]+)\}/);
|
|
3125
|
-
if (importMatch) {
|
|
3200
|
+
if (importMatch?.[1]) {
|
|
3126
3201
|
const imports = importMatch[1].split(",").map((imp) => imp.trim());
|
|
3127
3202
|
const usedImports = imports.filter((imp) => usedIdentifiers.has(imp));
|
|
3128
|
-
if (usedImports.length > 0) {
|
|
3203
|
+
if (usedImports.length > 0 && importMatch[1]) {
|
|
3129
3204
|
cleanedLines.push(line.replace(importMatch[1], usedImports.join(", ")));
|
|
3130
3205
|
} else {
|
|
3131
3206
|
continue;
|
|
@@ -3172,7 +3247,7 @@ var ActionGenerator = class {
|
|
|
3172
3247
|
if (!this.documentationEnabled) {
|
|
3173
3248
|
return "";
|
|
3174
3249
|
}
|
|
3175
|
-
const summary = endpoint.summary
|
|
3250
|
+
const summary = endpoint.summary ?? endpoint.description ?? `${endpoint.method} ${endpoint.path}`;
|
|
3176
3251
|
return `/**
|
|
3177
3252
|
* ${summary}
|
|
3178
3253
|
* @generated from ${endpoint.method} ${endpoint.path}
|
|
@@ -3314,7 +3389,7 @@ ${" ".repeat(indent + 2)}error: error instanceof Error ? error.message : 'Unknow
|
|
|
3314
3389
|
${" ".repeat(indent)}})`;
|
|
3315
3390
|
}
|
|
3316
3391
|
buildImportPath(relativePath) {
|
|
3317
|
-
const outputDirectory = this.configuration.outputDir
|
|
3392
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
3318
3393
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
3319
3394
|
let importBasePath = outputDirectory;
|
|
3320
3395
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -3322,7 +3397,7 @@ ${" ".repeat(indent)}})`;
|
|
|
3322
3397
|
}
|
|
3323
3398
|
return `@/${importBasePath}/${cleanPath}`;
|
|
3324
3399
|
}
|
|
3325
|
-
|
|
3400
|
+
generate(context) {
|
|
3326
3401
|
const { schema, config } = context;
|
|
3327
3402
|
const generatedFiles = [];
|
|
3328
3403
|
const endpointsByTag = this.groupEndpointsByTag(schema.endpoints);
|
|
@@ -3416,7 +3491,7 @@ ${actions}`;
|
|
|
3416
3491
|
dependencies: Object.keys(endpointsByTag).map((tag) => `./${toValidIdentifier(tag)}`)
|
|
3417
3492
|
}
|
|
3418
3493
|
});
|
|
3419
|
-
return generatedFiles;
|
|
3494
|
+
return Promise.resolve(generatedFiles);
|
|
3420
3495
|
}
|
|
3421
3496
|
getOperationName(endpoint) {
|
|
3422
3497
|
if (endpoint.operationId) {
|
|
@@ -3429,7 +3504,7 @@ ${actions}`;
|
|
|
3429
3504
|
const method = this.toPascalCase(endpoint.method.toLowerCase());
|
|
3430
3505
|
return [...pathParts, method].join("");
|
|
3431
3506
|
}
|
|
3432
|
-
generateOptimizedServerAction(endpoint,
|
|
3507
|
+
generateOptimizedServerAction(endpoint, _config) {
|
|
3433
3508
|
const actionName = toActionName(endpoint.operationId || endpoint.id);
|
|
3434
3509
|
const operationName = this.getOperationName(endpoint);
|
|
3435
3510
|
const hasRequestBody = !!endpoint.requestBody;
|
|
@@ -3470,10 +3545,15 @@ ${actions}`;
|
|
|
3470
3545
|
parameterProcessing = "";
|
|
3471
3546
|
requestOptionsParams = "";
|
|
3472
3547
|
}
|
|
3473
|
-
const
|
|
3548
|
+
const schemaConstantName = hasRequestBody && hasAnyParams ? `${operationName}InputSchema` : null;
|
|
3549
|
+
const schemaConstantDefinition = schemaConstantName ? `const ${schemaConstantName} = z.object({ body: ${operationName}RequestSchema, params: ${operationName}ParamsSchema })
|
|
3550
|
+
|
|
3551
|
+
` : "";
|
|
3552
|
+
const parsedInputType = hasRequestBody || hasAnyParams ? schemaConstantName ? `z.infer<typeof ${schemaConstantName}>` : `z.infer<typeof ${schemaName}>` : "void";
|
|
3474
3553
|
const ctxType = requiresAuth || hasRateLimit ? "{ user?: any; ratelimit?: { remaining: number } }" : "any";
|
|
3475
3554
|
const ctxDeclaration = requiresAuth || hasRateLimit ? `ctx: ${ctxType}` : "ctx?: any";
|
|
3476
|
-
const actionArgsType =
|
|
3555
|
+
const actionArgsType = schemaConstantName ? `{ parsedInput: z.infer<typeof ${schemaConstantName}>; ${ctxDeclaration} }` : `{ parsedInput: ${parsedInputType}; ${ctxDeclaration} }`;
|
|
3556
|
+
const actionArgsTypeForTemplate = actionArgsType.replace(/\n/g, " ").replace(/\s+/g, " ").trim();
|
|
3477
3557
|
const clientName = requiresAuth || hasRateLimit ? "authActionClient" : "actionClientWithMeta";
|
|
3478
3558
|
const rateLimitCode = hasRateLimit ? `
|
|
3479
3559
|
${this.commentLine("Rate limiting", 6)} const { userAgent, ip } = await getClientInfo()
|
|
@@ -3496,15 +3576,15 @@ ${this.commentLine("Rate limiting", 6)} const { userAgent, ip } = await get
|
|
|
3496
3576
|
endpoint,
|
|
3497
3577
|
"React cache, input validation, error handling"
|
|
3498
3578
|
);
|
|
3499
|
-
return `${docBlock}export const ${actionName} = cache(
|
|
3579
|
+
return `${docBlock}${schemaConstantDefinition}export const ${actionName} = cache(
|
|
3500
3580
|
${clientName}
|
|
3501
3581
|
.metadata({
|
|
3502
3582
|
name: "${kebabActionName}",
|
|
3503
3583
|
requiresAuth: ${requiresAuth}${hasRateLimit ? `,
|
|
3504
3584
|
rateLimit: { requests: ${endpoint.metadata.rateLimit?.requests || 10}, window: "${endpoint.metadata.rateLimit?.window || "1m"}" }` : ""}
|
|
3505
3585
|
})
|
|
3506
|
-
.schema(${
|
|
3507
|
-
.action(async ({ parsedInput, ctx }: ${
|
|
3586
|
+
.schema(${schemaConstantName || schemaName})
|
|
3587
|
+
.action(async ({ parsedInput, ctx }: ${actionArgsTypeForTemplate}) => {
|
|
3508
3588
|
const startTime = Date.now()
|
|
3509
3589
|
|
|
3510
3590
|
try {${rateLimitCode}${parameterProcessing}
|
|
@@ -3563,14 +3643,14 @@ ${this.commentLine("Process file with compression and validation if enabled", 10
|
|
|
3563
3643
|
endpoint,
|
|
3564
3644
|
"Input validation, revalidation, error handling"
|
|
3565
3645
|
);
|
|
3566
|
-
return `${mutationDocBlock}export const ${actionName} = ${clientName}
|
|
3646
|
+
return `${mutationDocBlock}${schemaConstantDefinition}export const ${actionName} = ${clientName}
|
|
3567
3647
|
.metadata({
|
|
3568
3648
|
name: "${kebabActionName}",
|
|
3569
3649
|
requiresAuth: ${requiresAuth}${hasRateLimit ? `,
|
|
3570
3650
|
rateLimit: { requests: ${endpoint.metadata.rateLimit?.requests || 10}, window: "${endpoint.metadata.rateLimit?.window || "1m"}" }` : ""}
|
|
3571
3651
|
})
|
|
3572
|
-
.schema(${
|
|
3573
|
-
.action(async ({ parsedInput, ctx }: ${
|
|
3652
|
+
.schema(${schemaConstantName || schemaName})
|
|
3653
|
+
.action(async ({ parsedInput, ctx }: ${actionArgsTypeForTemplate}) => {
|
|
3574
3654
|
const startTime = Date.now()
|
|
3575
3655
|
|
|
3576
3656
|
try {${rateLimitCode}${parameterProcessing}${fileUploadCode}
|
|
@@ -3698,7 +3778,7 @@ var HookGenerator = class {
|
|
|
3698
3778
|
__name(this, "HookGenerator");
|
|
3699
3779
|
}
|
|
3700
3780
|
buildImportPath(relativePath) {
|
|
3701
|
-
const outputDirectory = this.configuration.outputDir
|
|
3781
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
3702
3782
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
3703
3783
|
let importBasePath = outputDirectory;
|
|
3704
3784
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -3706,7 +3786,7 @@ var HookGenerator = class {
|
|
|
3706
3786
|
}
|
|
3707
3787
|
return `@/${importBasePath}/${cleanPath}`;
|
|
3708
3788
|
}
|
|
3709
|
-
|
|
3789
|
+
generate(context) {
|
|
3710
3790
|
const { schema } = context;
|
|
3711
3791
|
const generatedFiles = [];
|
|
3712
3792
|
const endpointsByTag = this.groupEndpointsByTag(schema.endpoints);
|
|
@@ -3839,7 +3919,7 @@ ${Object.keys(endpointsByTag).map((tag) => `export * from './${toValidIdentifier
|
|
|
3839
3919
|
dependencies: ["./useBridgeQuery", ...Object.keys(endpointsByTag).map((tag) => `./${toValidIdentifier(tag)}`)]
|
|
3840
3920
|
}
|
|
3841
3921
|
});
|
|
3842
|
-
return generatedFiles;
|
|
3922
|
+
return Promise.resolve(generatedFiles);
|
|
3843
3923
|
}
|
|
3844
3924
|
getOperationName(endpoint) {
|
|
3845
3925
|
if (endpoint.operationId) {
|
|
@@ -3856,44 +3936,65 @@ ${Object.keys(endpointsByTag).map((tag) => `export * from './${toValidIdentifier
|
|
|
3856
3936
|
const hookName = toHookName(endpoint.operationId || endpoint.id, true);
|
|
3857
3937
|
const actionName = toActionName(endpoint.operationId || endpoint.id);
|
|
3858
3938
|
const operationName = this.getOperationName(endpoint);
|
|
3859
|
-
const queryKey = this.generateQueryKey(endpoint);
|
|
3860
3939
|
const staleTime = this.getStaleTime(endpoint);
|
|
3861
3940
|
const pathParameters = endpoint.parameters.filter((parameter) => parameter.in === "path");
|
|
3862
3941
|
const queryParameters = endpoint.parameters.filter((parameter) => parameter.in === "query");
|
|
3863
3942
|
const hasPageParam = queryParameters.some((param) => param.name === "page");
|
|
3864
3943
|
const hasSearchParams = endpoint.path.includes("search") || endpoint.path.includes("list");
|
|
3865
|
-
|
|
3866
|
-
const
|
|
3944
|
+
const pathParamTypes = [];
|
|
3945
|
+
const queryParamTypes = [];
|
|
3867
3946
|
pathParameters.forEach((param) => {
|
|
3868
3947
|
const isRequired = param.required ? "" : "?";
|
|
3869
3948
|
const paramType = this.getTypeFromZodSchema(param.schema);
|
|
3870
|
-
|
|
3949
|
+
pathParamTypes.push(`${param.name}${isRequired}: ${paramType}`);
|
|
3871
3950
|
});
|
|
3872
3951
|
queryParameters.forEach((param) => {
|
|
3873
3952
|
const isRequired = param.required ? "" : "?";
|
|
3874
3953
|
const paramType = this.getTypeFromZodSchema(param.schema);
|
|
3875
|
-
|
|
3954
|
+
queryParamTypes.push(`${param.name}${isRequired}: ${paramType}`);
|
|
3876
3955
|
});
|
|
3877
3956
|
const returnType = `z.infer<typeof ${operationName}ResponseSchema>`;
|
|
3878
3957
|
const optionsType = `{ enabled?: boolean; suspense?: boolean; refetchInterval?: number; initialData?: ${returnType} }`;
|
|
3879
|
-
const
|
|
3880
|
-
|
|
3958
|
+
const paramsTypeParts = [];
|
|
3959
|
+
if (pathParameters.length > 0) {
|
|
3960
|
+
paramsTypeParts.push(`path: { ${pathParamTypes.join("; ")} }`);
|
|
3961
|
+
}
|
|
3962
|
+
if (queryParameters.length > 0) {
|
|
3963
|
+
paramsTypeParts.push(`query?: { ${queryParamTypes.join("; ")} }`);
|
|
3964
|
+
}
|
|
3965
|
+
const paramsType = paramsTypeParts.length > 0 ? `{ ${paramsTypeParts.join("; ")} }` : "{}";
|
|
3966
|
+
const enabledCondition = pathParameters.length > 0 && pathParameters[0] ? `!!params?.path?.${pathParameters[0].name}` : "true";
|
|
3967
|
+
const pathParamsBuild = pathParameters.length > 0 ? `{ ${pathParameters.map((p) => `${p.name}: params.path.${p.name}`).join(", ")} }` : "{}";
|
|
3968
|
+
const queryParamsBuild = queryParameters.length > 0 ? `{ ${queryParameters.map((p) => `${p.name}: params?.query?.${p.name}`).join(", ")} }` : "{}";
|
|
3881
3969
|
if (hasSearchParams) {
|
|
3882
|
-
const
|
|
3970
|
+
const queryParamsWithFallback = queryParameters.length > 0 ? `const queryParams = {
|
|
3971
|
+
${queryParameters.map((param) => {
|
|
3972
|
+
if (param.name === "query") {
|
|
3973
|
+
return `${param.name}: params?.query?.${param.name} || searchParams.search || ''`;
|
|
3974
|
+
} else if (param.name === "limit") {
|
|
3975
|
+
return `${param.name}: params?.query?.${param.name} !== undefined ? params.query.${param.name} : searchParams.limit`;
|
|
3976
|
+
} else {
|
|
3977
|
+
return `${param.name}: params?.query?.${param.name}`;
|
|
3978
|
+
}
|
|
3979
|
+
}).join(",\n ")}
|
|
3980
|
+
}` : "const queryParams = {}";
|
|
3883
3981
|
return `/**
|
|
3884
3982
|
* Optimized query hook for ${endpoint.method} ${endpoint.path}
|
|
3885
3983
|
* Features: URL state sync, infinite loading, optimistic updates
|
|
3984
|
+
* @param params - Named parameters object with path and query parameters
|
|
3985
|
+
* @param options - Query options
|
|
3886
3986
|
* @returns useQuery result with data of type ${returnType}
|
|
3887
3987
|
*/
|
|
3888
|
-
export function ${hookName}(${
|
|
3988
|
+
export function ${hookName}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
3889
3989
|
const [searchParams] = useQueryStates(searchParamsParser)
|
|
3890
3990
|
const { initialData, ...restOptions } = options ?? {}
|
|
3891
3991
|
|
|
3892
3992
|
return useQuery({
|
|
3893
|
-
queryKey: [
|
|
3993
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query, searchParams],
|
|
3894
3994
|
queryFn: async ({ signal }: { signal?: AbortSignal }) => {
|
|
3895
3995
|
try {
|
|
3896
|
-
|
|
3996
|
+
${queryParamsWithFallback}
|
|
3997
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: queryParams }` : "{ query: queryParams }"}))
|
|
3897
3998
|
return result
|
|
3898
3999
|
} catch (error) {
|
|
3899
4000
|
handleActionError(error)
|
|
@@ -3921,29 +4022,33 @@ export function ${hookName}(${parameterTypes.length > 0 ? `${parameterTypes.join
|
|
|
3921
4022
|
|
|
3922
4023
|
/**
|
|
3923
4024
|
* Infinite query version for paginated ${endpoint.path}
|
|
4025
|
+
* @param params - Named parameters object with path and query parameters
|
|
4026
|
+
* @param options - Query options
|
|
3924
4027
|
* @returns useInfiniteQuery result with data of type ${returnType}
|
|
3925
4028
|
*/
|
|
3926
|
-
export function ${hookName.replace("use", "useInfinite")}(${
|
|
4029
|
+
export function ${hookName.replace("use", "useInfinite")}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
3927
4030
|
const [searchParams] = useQueryStates(searchParamsParser)
|
|
3928
4031
|
const { initialData, ...restOptions } = options ?? {}
|
|
3929
4032
|
|
|
3930
4033
|
return useInfiniteQuery({
|
|
3931
|
-
queryKey: [
|
|
4034
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query, 'infinite', searchParams],
|
|
3932
4035
|
initialPageParam: 1,
|
|
3933
4036
|
queryFn: async ({ pageParam = 1, signal }: { pageParam?: number; signal?: AbortSignal }) => {
|
|
3934
4037
|
try {
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
${queryParameters.map((param) => {
|
|
4038
|
+
const queryParams = {
|
|
4039
|
+
${queryParameters.map((param) => {
|
|
3938
4040
|
if (param.name === "page" && hasPageParam) {
|
|
3939
|
-
return
|
|
4041
|
+
return `${param.name}: pageParam`;
|
|
4042
|
+
} else if (param.name === "query") {
|
|
4043
|
+
return `${param.name}: params?.query?.${param.name} || searchParams.search || ''`;
|
|
3940
4044
|
} else if (param.name === "limit") {
|
|
3941
|
-
return
|
|
4045
|
+
return `${param.name}: params?.query?.${param.name} !== undefined ? params.query.${param.name} : searchParams.limit`;
|
|
3942
4046
|
} else {
|
|
3943
|
-
return
|
|
4047
|
+
return `${param.name}: params?.query?.${param.name}`;
|
|
4048
|
+
}
|
|
4049
|
+
}).join(",\n ")}
|
|
3944
4050
|
}
|
|
3945
|
-
|
|
3946
|
-
const result = await resolveActionResult<${returnType}>(${actionName}(${actionCallParams.replace(queryParamObject, "queryParams")}))
|
|
4051
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: queryParams }` : "{ query: queryParams }"}))
|
|
3947
4052
|
return result
|
|
3948
4053
|
} catch (error) {
|
|
3949
4054
|
handleActionError(error)
|
|
@@ -3985,15 +4090,17 @@ export function ${hookName.replace("use", "useInfinite")}(${parameterTypes.lengt
|
|
|
3985
4090
|
|
|
3986
4091
|
/**
|
|
3987
4092
|
* Suspense version for ${endpoint.path} - use in Server Components
|
|
4093
|
+
* @param params - Named parameters object with path and query parameters
|
|
4094
|
+
* @param options - Query options
|
|
3988
4095
|
* @returns useSuspenseQuery result with data of type ${returnType}
|
|
3989
4096
|
*/
|
|
3990
|
-
export function ${hookName.replace("use", "useSuspense")}(${
|
|
4097
|
+
export function ${hookName.replace("use", "useSuspense")}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
3991
4098
|
const { initialData, ...restOptions } = options ?? {}
|
|
3992
4099
|
|
|
3993
4100
|
return useSuspenseQuery({
|
|
3994
|
-
queryKey: ${
|
|
4101
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query],
|
|
3995
4102
|
queryFn: async () => {
|
|
3996
|
-
const result = await resolveActionResult<${returnType}>(${actionName}(${
|
|
4103
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: ${queryParamsBuild} }` : `{ query: ${queryParamsBuild} }`}))
|
|
3997
4104
|
return result
|
|
3998
4105
|
},
|
|
3999
4106
|
staleTime: ${staleTime},
|
|
@@ -4005,16 +4112,18 @@ export function ${hookName.replace("use", "useSuspense")}(${parameterTypes.lengt
|
|
|
4005
4112
|
return `/**
|
|
4006
4113
|
* Optimized query hook for ${endpoint.method} ${endpoint.path}
|
|
4007
4114
|
* Features: Smart caching, error handling, type safety
|
|
4115
|
+
* @param params - Named parameters object with path and query parameters
|
|
4116
|
+
* @param options - Query options
|
|
4008
4117
|
* @returns useQuery result with data of type ${returnType}
|
|
4009
4118
|
*/
|
|
4010
|
-
export function ${hookName}(${
|
|
4119
|
+
export function ${hookName}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
4011
4120
|
const { initialData, ...restOptions } = options ?? {}
|
|
4012
4121
|
|
|
4013
4122
|
return useQuery({
|
|
4014
|
-
queryKey: ${
|
|
4123
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query],
|
|
4015
4124
|
queryFn: async ({ signal }: { signal?: AbortSignal }) => {
|
|
4016
4125
|
try {
|
|
4017
|
-
const result = await resolveActionResult<${returnType}>(${
|
|
4126
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: ${queryParamsBuild} }` : `{ query: ${queryParamsBuild} }`}))
|
|
4018
4127
|
return result
|
|
4019
4128
|
} catch (error) {
|
|
4020
4129
|
handleActionError(error)
|
|
@@ -4042,15 +4151,17 @@ export function ${hookName}(${parameterTypes.length > 0 ? `${parameterTypes.join
|
|
|
4042
4151
|
|
|
4043
4152
|
/**
|
|
4044
4153
|
* Suspense version for ${endpoint.path}
|
|
4154
|
+
* @param params - Named parameters object with path and query parameters
|
|
4155
|
+
* @param options - Query options
|
|
4045
4156
|
* @returns useSuspenseQuery result with data of type ${returnType}
|
|
4046
4157
|
*/
|
|
4047
|
-
export function ${hookName.replace("use", "useSuspense")}(${
|
|
4158
|
+
export function ${hookName.replace("use", "useSuspense")}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
4048
4159
|
const { initialData, ...restOptions } = options ?? {}
|
|
4049
4160
|
|
|
4050
4161
|
return useSuspenseQuery({
|
|
4051
|
-
queryKey: ${
|
|
4162
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query],
|
|
4052
4163
|
queryFn: async () => {
|
|
4053
|
-
const result = await resolveActionResult<${returnType}>(${
|
|
4164
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: ${queryParamsBuild} }` : `{ query: ${queryParamsBuild} }`}))
|
|
4054
4165
|
return result
|
|
4055
4166
|
},
|
|
4056
4167
|
staleTime: ${staleTime},
|
|
@@ -4165,7 +4276,7 @@ ${invalidationCode}
|
|
|
4165
4276
|
* Find related queries that should be invalidated when this mutation runs
|
|
4166
4277
|
*/
|
|
4167
4278
|
findRelatedQueries(endpoint, context) {
|
|
4168
|
-
if (!context?.schema
|
|
4279
|
+
if (!context?.schema.endpoints) {
|
|
4169
4280
|
return [];
|
|
4170
4281
|
}
|
|
4171
4282
|
const relatedQueries = [];
|
|
@@ -4299,7 +4410,6 @@ ${invalidationCode}
|
|
|
4299
4410
|
buildActionCallParams(endpoint, isMutation) {
|
|
4300
4411
|
const pathParameters = endpoint.parameters.filter((parameter) => parameter.in === "path");
|
|
4301
4412
|
const queryParameters = endpoint.parameters.filter((parameter) => parameter.in === "query");
|
|
4302
|
-
!!endpoint.requestBody;
|
|
4303
4413
|
if (isMutation) {
|
|
4304
4414
|
return "variables";
|
|
4305
4415
|
}
|
|
@@ -4380,13 +4490,13 @@ ${invalidationCode}
|
|
|
4380
4490
|
import { useQuery, useMutation, useInfiniteQuery, useSuspenseQuery, type UseQueryOptions, type UseMutationOptions, type UseInfiniteQueryOptions, type UseSuspenseQueryOptions, type QueryKey, type QueryFunction } from '@tanstack/react-query'
|
|
4381
4491
|
|
|
4382
4492
|
/**
|
|
4383
|
-
* Enhanced React Query wrapper hook with Next.js 16.0.
|
|
4493
|
+
* Enhanced React Query wrapper hook with Next.js 16.0.7 optimizations
|
|
4384
4494
|
* Provides consistent defaults across all queries following best practices
|
|
4385
4495
|
*
|
|
4386
4496
|
* Features:
|
|
4387
4497
|
* - React Query v5: Uses gcTime instead of cacheTime
|
|
4388
4498
|
* - React Query v5: Uses placeholderData instead of keepPreviousData
|
|
4389
|
-
* - Next.js 16.0.
|
|
4499
|
+
* - Next.js 16.0.7: Optimized for App Router and Server Components
|
|
4390
4500
|
*/
|
|
4391
4501
|
export function useBridgeQuery<TData = unknown, TError = Error>(
|
|
4392
4502
|
queryKey: QueryKey,
|
|
@@ -4401,14 +4511,8 @@ export function useBridgeQuery<TData = unknown, TError = Error>(
|
|
|
4401
4511
|
refetchOnWindowFocus: true,
|
|
4402
4512
|
refetchOnReconnect: true,
|
|
4403
4513
|
refetchOnMount: 'always',
|
|
4404
|
-
// React Query v5: placeholderData
|
|
4405
|
-
|
|
4406
|
-
placeholderData: (previousData) => {
|
|
4407
|
-
if (previousData === undefined) return undefined
|
|
4408
|
-
// Type guard: ensure we don't return a function (React Query v5 requirement)
|
|
4409
|
-
if (typeof previousData === 'function') return undefined
|
|
4410
|
-
return previousData
|
|
4411
|
-
} as any,
|
|
4514
|
+
// React Query v5: placeholderData returns previous data if available
|
|
4515
|
+
placeholderData: (previousData) => previousData,
|
|
4412
4516
|
retry: (failureCount: number, error: TError) => {
|
|
4413
4517
|
if (error instanceof Error && error.message.includes('4')) return false
|
|
4414
4518
|
return failureCount < 3
|
|
@@ -4421,15 +4525,17 @@ export function useBridgeQuery<TData = unknown, TError = Error>(
|
|
|
4421
4525
|
* Enhanced infinite query wrapper
|
|
4422
4526
|
* React Query v5: Optimized for paginated data with infinite scrolling
|
|
4423
4527
|
*/
|
|
4424
|
-
export function useBridgeInfiniteQuery<TData = unknown, TError = Error>(
|
|
4528
|
+
export function useBridgeInfiniteQuery<TData = unknown, TError = Error, TPageParam = number>(
|
|
4425
4529
|
queryKey: QueryKey,
|
|
4426
|
-
queryFn: QueryFunction<TData, QueryKey>,
|
|
4427
|
-
options?: Partial<UseInfiniteQueryOptions<TData, TError, TData, QueryKey>>
|
|
4530
|
+
queryFn: QueryFunction<TData, QueryKey, TPageParam>,
|
|
4531
|
+
options?: Partial<UseInfiniteQueryOptions<TData, TError, TData, QueryKey, TPageParam>> & {
|
|
4532
|
+
getNextPageParam?: (lastPage: TData, allPages: TData[]) => TPageParam | undefined
|
|
4533
|
+
}
|
|
4428
4534
|
) {
|
|
4429
|
-
return useInfiniteQuery<TData, TError>({
|
|
4535
|
+
return useInfiniteQuery<TData, TError, TData, QueryKey, TPageParam>({
|
|
4430
4536
|
queryKey,
|
|
4431
|
-
initialPageParam: 1,
|
|
4432
|
-
queryFn
|
|
4537
|
+
initialPageParam: 1 as TPageParam,
|
|
4538
|
+
queryFn,
|
|
4433
4539
|
staleTime: 5 * 60 * 1000,
|
|
4434
4540
|
gcTime: 10 * 60 * 1000,
|
|
4435
4541
|
refetchOnWindowFocus: true,
|
|
@@ -4439,13 +4545,15 @@ export function useBridgeInfiniteQuery<TData = unknown, TError = Error>(
|
|
|
4439
4545
|
if (error instanceof Error && error.message.includes('4')) return false
|
|
4440
4546
|
return failureCount < 3
|
|
4441
4547
|
},
|
|
4548
|
+
// Provide default getNextPageParam if not provided
|
|
4549
|
+
getNextPageParam: options?.getNextPageParam || (() => undefined),
|
|
4442
4550
|
...options,
|
|
4443
4551
|
})
|
|
4444
4552
|
}
|
|
4445
4553
|
|
|
4446
4554
|
/**
|
|
4447
4555
|
* Enhanced suspense query wrapper
|
|
4448
|
-
* Next.js 16.0.
|
|
4556
|
+
* Next.js 16.0.7: Optimized for Server Components with Suspense
|
|
4449
4557
|
*/
|
|
4450
4558
|
export function useBridgeSuspenseQuery<TData = unknown, TError = Error>(
|
|
4451
4559
|
queryKey: QueryKey,
|
|
@@ -4501,7 +4609,7 @@ var AuthGenerator = class {
|
|
|
4501
4609
|
__name(this, "AuthGenerator");
|
|
4502
4610
|
}
|
|
4503
4611
|
buildImportPath(relativePath) {
|
|
4504
|
-
const outputDirectory = this.configuration.outputDir
|
|
4612
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
4505
4613
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
4506
4614
|
let importBasePath = outputDirectory;
|
|
4507
4615
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -4509,11 +4617,11 @@ var AuthGenerator = class {
|
|
|
4509
4617
|
}
|
|
4510
4618
|
return `@/${importBasePath}/${cleanPath}`;
|
|
4511
4619
|
}
|
|
4512
|
-
|
|
4620
|
+
generate(context) {
|
|
4513
4621
|
const { config, schema } = context;
|
|
4514
4622
|
const authConfig = config.auth;
|
|
4515
4623
|
if (!authConfig?.enabled) {
|
|
4516
|
-
return [];
|
|
4624
|
+
return Promise.resolve([]);
|
|
4517
4625
|
}
|
|
4518
4626
|
const files = [];
|
|
4519
4627
|
const authEndpoints = this.findAuthEndpoints(schema.endpoints);
|
|
@@ -4528,7 +4636,7 @@ var AuthGenerator = class {
|
|
|
4528
4636
|
files.push(...this.generateAuthComponents(authEndpoints));
|
|
4529
4637
|
files.push(this.generateAuthMiddleware());
|
|
4530
4638
|
files.push(this.generateAuthContext());
|
|
4531
|
-
return files;
|
|
4639
|
+
return Promise.resolve(files);
|
|
4532
4640
|
}
|
|
4533
4641
|
findAuthEndpoints(endpoints) {
|
|
4534
4642
|
const authEndpoints = {};
|
|
@@ -4561,12 +4669,12 @@ var AuthGenerator = class {
|
|
|
4561
4669
|
}
|
|
4562
4670
|
return userEndpoints;
|
|
4563
4671
|
}
|
|
4564
|
-
generateAuthSchemas(
|
|
4672
|
+
generateAuthSchemas(_authEndpoints, _schema) {
|
|
4565
4673
|
this.buildImportPath("schemas");
|
|
4566
|
-
const loginRequestSchema =
|
|
4674
|
+
const loginRequestSchema = _authEndpoints.login?.requestBody?.content?.find(
|
|
4567
4675
|
(c) => c.type === "application/json"
|
|
4568
4676
|
)?.schema;
|
|
4569
|
-
const loginResponseSchema =
|
|
4677
|
+
const loginResponseSchema = _authEndpoints.login?.responses?.find(
|
|
4570
4678
|
(r) => r.statusCode === "200"
|
|
4571
4679
|
)?.content?.[0]?.schema;
|
|
4572
4680
|
const content = `import { z } from "zod"
|
|
@@ -4667,7 +4775,7 @@ export type Session = z.infer<typeof sessionSchema>`;
|
|
|
4667
4775
|
}
|
|
4668
4776
|
};
|
|
4669
4777
|
}
|
|
4670
|
-
generateAuthTypes(
|
|
4778
|
+
generateAuthTypes(_authEndpoints, _schema) {
|
|
4671
4779
|
const schemasImport = this.buildImportPath("auth/schemas");
|
|
4672
4780
|
const content = `// Auto-generated authentication types
|
|
4673
4781
|
import type {
|
|
@@ -5213,7 +5321,7 @@ export const useRequireAuth = (redirectTo?: string) => {
|
|
|
5213
5321
|
}
|
|
5214
5322
|
};
|
|
5215
5323
|
}
|
|
5216
|
-
generateAuthActions(
|
|
5324
|
+
generateAuthActions(_authEndpoints, _userEndpoints) {
|
|
5217
5325
|
const safeActionImport = this.buildImportPath("lib/safe-action");
|
|
5218
5326
|
const schemasImport = this.buildImportPath("auth/schemas");
|
|
5219
5327
|
const clientImport = this.buildImportPath("auth/client");
|
|
@@ -5445,7 +5553,7 @@ export const useCredentialsLogin = () => {
|
|
|
5445
5553
|
}
|
|
5446
5554
|
};
|
|
5447
5555
|
}
|
|
5448
|
-
generateAuthComponents(
|
|
5556
|
+
generateAuthComponents(_authEndpoints) {
|
|
5449
5557
|
const loginForm = {
|
|
5450
5558
|
path: "auth/components/login-form.tsx",
|
|
5451
5559
|
content: `"use client"
|
|
@@ -5565,7 +5673,6 @@ export function LoginForm() {
|
|
|
5565
5673
|
return [loginForm];
|
|
5566
5674
|
}
|
|
5567
5675
|
generateAuthMiddleware() {
|
|
5568
|
-
this.buildImportPath("auth/utils");
|
|
5569
5676
|
const content = `import { NextResponse } from "next/server"
|
|
5570
5677
|
import type { NextRequest } from "next/server"
|
|
5571
5678
|
|
|
@@ -5933,9 +6040,10 @@ var SchemaAnalyzer = class {
|
|
|
5933
6040
|
}
|
|
5934
6041
|
if (response.description) {
|
|
5935
6042
|
const errorMatch = response.description.match(/error[_\s]?code[:\s]+(\w+)/i);
|
|
5936
|
-
if (errorMatch) {
|
|
5937
|
-
|
|
5938
|
-
|
|
6043
|
+
if (errorMatch?.[1]) {
|
|
6044
|
+
const errorCode = errorMatch[1];
|
|
6045
|
+
errorCodes.set(errorCode.toLowerCase(), {
|
|
6046
|
+
code: errorCode,
|
|
5939
6047
|
status,
|
|
5940
6048
|
message: response.description
|
|
5941
6049
|
});
|
|
@@ -6021,7 +6129,7 @@ var SchemaAnalyzer = class {
|
|
|
6021
6129
|
const endpoints = {};
|
|
6022
6130
|
for (const endpoint of this.schema.endpoints) {
|
|
6023
6131
|
const path3 = endpoint.path.toLowerCase();
|
|
6024
|
-
const operationId = (endpoint.operationId
|
|
6132
|
+
const operationId = (endpoint.operationId ?? "").toLowerCase();
|
|
6025
6133
|
if ((path3.includes("/auth/login/credentials") || operationId.includes("logincredentials") || operationId.includes("signin")) && endpoint.method === "POST") {
|
|
6026
6134
|
endpoints.loginCredentials = endpoint;
|
|
6027
6135
|
} else if ((path3.includes("/auth/login") || operationId.includes("login")) && endpoint.method === "POST" && !endpoints.loginCredentials) {
|
|
@@ -6069,12 +6177,12 @@ var SchemaAnalyzer = class {
|
|
|
6069
6177
|
extractSessionConfig() {
|
|
6070
6178
|
const config = {};
|
|
6071
6179
|
const sessionEndpoints = this.schema.endpoints.filter(
|
|
6072
|
-
(e) => e.path.toLowerCase().includes("/session") || (e.operationId
|
|
6180
|
+
(e) => e.path.toLowerCase().includes("/session") || (e.operationId ?? "").toLowerCase().includes("session")
|
|
6073
6181
|
);
|
|
6074
6182
|
for (const endpoint of sessionEndpoints) {
|
|
6075
6183
|
for (const response of endpoint.responses) {
|
|
6076
6184
|
if (response.headers) {
|
|
6077
|
-
for (const [headerName,
|
|
6185
|
+
for (const [headerName, _headerDef] of Object.entries(response.headers)) {
|
|
6078
6186
|
if (headerName.toLowerCase().includes("expires") || headerName.toLowerCase().includes("max-age")) ;
|
|
6079
6187
|
}
|
|
6080
6188
|
}
|
|
@@ -6154,7 +6262,7 @@ var SchemaAnalyzer = class {
|
|
|
6154
6262
|
/**
|
|
6155
6263
|
* Check if OAuth provider is supported
|
|
6156
6264
|
*/
|
|
6157
|
-
isOAuthProviderSupported(
|
|
6265
|
+
isOAuthProviderSupported(_provider) {
|
|
6158
6266
|
const extractedEndpoints = this.extractAuthEndpoints();
|
|
6159
6267
|
return !!(extractedEndpoints.oauthAuthorize && extractedEndpoints.oauthCallback);
|
|
6160
6268
|
}
|
|
@@ -6236,7 +6344,7 @@ var SchemaAnalyzer = class {
|
|
|
6236
6344
|
errorCodes.set(code.toLowerCase(), {
|
|
6237
6345
|
code,
|
|
6238
6346
|
status,
|
|
6239
|
-
message: schema.properties?.detail?.default
|
|
6347
|
+
message: schema.properties?.detail?.default ?? schema.properties?.message?.default ?? ""
|
|
6240
6348
|
});
|
|
6241
6349
|
});
|
|
6242
6350
|
}
|
|
@@ -6245,9 +6353,10 @@ var SchemaAnalyzer = class {
|
|
|
6245
6353
|
const detail = schema.properties.detail;
|
|
6246
6354
|
if (typeof detail === "string") {
|
|
6247
6355
|
const codeMatch = detail.match(/(\w+)[_\s]?error/i);
|
|
6248
|
-
if (codeMatch) {
|
|
6249
|
-
|
|
6250
|
-
|
|
6356
|
+
if (codeMatch?.[1]) {
|
|
6357
|
+
const errorCode = codeMatch[1];
|
|
6358
|
+
errorCodes.set(errorCode.toLowerCase(), {
|
|
6359
|
+
code: errorCode,
|
|
6251
6360
|
status,
|
|
6252
6361
|
message: detail
|
|
6253
6362
|
});
|
|
@@ -6259,11 +6368,11 @@ var SchemaAnalyzer = class {
|
|
|
6259
6368
|
if (!schema || typeof schema !== "object") return;
|
|
6260
6369
|
if (schema.properties?.file) {
|
|
6261
6370
|
const fileSchema = schema.properties.file;
|
|
6262
|
-
if (fileSchema.maxSize
|
|
6263
|
-
config.maxSize = fileSchema.maxSize
|
|
6371
|
+
if (fileSchema.maxSize ?? fileSchema["x-max-size"]) {
|
|
6372
|
+
config.maxSize = fileSchema.maxSize ?? fileSchema["x-max-size"];
|
|
6264
6373
|
}
|
|
6265
|
-
if (fileSchema.allowedTypes
|
|
6266
|
-
config.allowedTypes = fileSchema.allowedTypes
|
|
6374
|
+
if (fileSchema.allowedTypes ?? fileSchema["x-allowed-types"]) {
|
|
6375
|
+
config.allowedTypes = fileSchema.allowedTypes ?? fileSchema["x-allowed-types"];
|
|
6267
6376
|
}
|
|
6268
6377
|
}
|
|
6269
6378
|
if (schema["x-file-constraints"]) {
|
|
@@ -6283,7 +6392,7 @@ var NextAuthGenerator = class {
|
|
|
6283
6392
|
analyzer;
|
|
6284
6393
|
accountStatusPatterns;
|
|
6285
6394
|
buildImportPath(relativePath) {
|
|
6286
|
-
const outputDirectory = this.configuration.outputDir
|
|
6395
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
6287
6396
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
6288
6397
|
let importBasePath = outputDirectory;
|
|
6289
6398
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -6291,11 +6400,11 @@ var NextAuthGenerator = class {
|
|
|
6291
6400
|
}
|
|
6292
6401
|
return `@/${importBasePath}/${cleanPath}`;
|
|
6293
6402
|
}
|
|
6294
|
-
|
|
6403
|
+
generate(context) {
|
|
6295
6404
|
const { config, schema } = context;
|
|
6296
6405
|
const authConfig = config.auth;
|
|
6297
6406
|
if (!authConfig?.enabled || authConfig.provider !== "next-auth") {
|
|
6298
|
-
return [];
|
|
6407
|
+
return Promise.resolve([]);
|
|
6299
6408
|
}
|
|
6300
6409
|
this.analyzer = new SchemaAnalyzer(schema);
|
|
6301
6410
|
this.accountStatusPatterns = this.analyzer.extractAccountStatusPatterns();
|
|
@@ -6312,7 +6421,7 @@ var NextAuthGenerator = class {
|
|
|
6312
6421
|
console.warn("These features will be disabled in generated code.\n");
|
|
6313
6422
|
}
|
|
6314
6423
|
const detectedOAuthProviders = this.analyzer.extractOAuthProviders();
|
|
6315
|
-
const configuredOAuthProviders = authConfig.oauth?.providers
|
|
6424
|
+
const configuredOAuthProviders = authConfig.oauth?.providers ?? [];
|
|
6316
6425
|
const validOAuthProviders = configuredOAuthProviders.filter(
|
|
6317
6426
|
(provider) => this.analyzer.isOAuthProviderSupported(provider)
|
|
6318
6427
|
);
|
|
@@ -6323,7 +6432,7 @@ var NextAuthGenerator = class {
|
|
|
6323
6432
|
console.warn(
|
|
6324
6433
|
`\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.`
|
|
6325
6434
|
);
|
|
6326
|
-
return files;
|
|
6435
|
+
return Promise.resolve(files);
|
|
6327
6436
|
}
|
|
6328
6437
|
files.push(this.generateNextAuthConfig(
|
|
6329
6438
|
extractedEndpoints,
|
|
@@ -6336,7 +6445,7 @@ var NextAuthGenerator = class {
|
|
|
6336
6445
|
if (hasPasskeys) {
|
|
6337
6446
|
files.push(this.generatePasskeyUtils(extractedEndpoints));
|
|
6338
6447
|
}
|
|
6339
|
-
return files;
|
|
6448
|
+
return Promise.resolve(files);
|
|
6340
6449
|
}
|
|
6341
6450
|
findAuthEndpoints(endpoints) {
|
|
6342
6451
|
const authEndpoints = {};
|
|
@@ -6439,14 +6548,14 @@ var NextAuthGenerator = class {
|
|
|
6439
6548
|
}
|
|
6440
6549
|
generateNextAuthConfig(extractedEndpoints, oauthProviders, hasMFA, hasPasskeys) {
|
|
6441
6550
|
const authConfig = this.configuration.auth;
|
|
6442
|
-
const apiBaseUrl = this.configuration.api
|
|
6551
|
+
const apiBaseUrl = this.configuration.api.baseUrl || "http://localhost:8000";
|
|
6443
6552
|
const apiUrl = `process.env.NEXT_PUBLIC_API_URL || '${apiBaseUrl}'`;
|
|
6444
6553
|
const hasGoogle = oauthProviders.includes("google");
|
|
6445
6554
|
const hasGitHub = oauthProviders.includes("github");
|
|
6446
6555
|
oauthProviders.includes("discord");
|
|
6447
6556
|
oauthProviders.includes("microsoft");
|
|
6448
6557
|
oauthProviders.includes("apple");
|
|
6449
|
-
const { activeStatuses, inactiveStatuses, suspendedStatuses, lockedStatuses } = this.accountStatusPatterns;
|
|
6558
|
+
const { activeStatuses, inactiveStatuses, suspendedStatuses, lockedStatuses: _lockedStatuses } = this.accountStatusPatterns;
|
|
6450
6559
|
this.analyzer.extractErrorCodes();
|
|
6451
6560
|
const sessionStrategy = authConfig.session?.strategy || "jwt";
|
|
6452
6561
|
const sessionMaxAge = authConfig.session?.maxAge || 7 * 24 * 60 * 60;
|
|
@@ -6995,7 +7104,7 @@ export async function authenticateWithPasskey(email?: string) {
|
|
|
6995
7104
|
}
|
|
6996
7105
|
};
|
|
6997
7106
|
}
|
|
6998
|
-
generateAuthTypes(
|
|
7107
|
+
generateAuthTypes(_authEndpoints, _accountStatusPatterns) {
|
|
6999
7108
|
const content = `/**
|
|
7000
7109
|
* Authentication Types
|
|
7001
7110
|
* Auto-generated by Mulink
|
|
@@ -7066,7 +7175,7 @@ export class AccountInactiveError extends AuthError {
|
|
|
7066
7175
|
}
|
|
7067
7176
|
};
|
|
7068
7177
|
}
|
|
7069
|
-
generateAuthUtils(
|
|
7178
|
+
generateAuthUtils(_authEndpoints) {
|
|
7070
7179
|
const content = `/**
|
|
7071
7180
|
* Authentication Utilities
|
|
7072
7181
|
* Auto-generated by Mulink
|
|
@@ -7119,7 +7228,7 @@ export async function requireAuth() {
|
|
|
7119
7228
|
}
|
|
7120
7229
|
};
|
|
7121
7230
|
}
|
|
7122
|
-
generatePasskeyUtils(
|
|
7231
|
+
generatePasskeyUtils(_passkeyEndpoints) {
|
|
7123
7232
|
const content = `/**
|
|
7124
7233
|
* Passkey (WebAuthn) Utilities
|
|
7125
7234
|
* Auto-generated by Mulink
|
|
@@ -7172,7 +7281,7 @@ var MiddlewareGenerator = class {
|
|
|
7172
7281
|
__name(this, "MiddlewareGenerator");
|
|
7173
7282
|
}
|
|
7174
7283
|
buildImportPath(relativePath) {
|
|
7175
|
-
const outputDirectory = this.configuration.outputDir
|
|
7284
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
7176
7285
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
7177
7286
|
let importBasePath = outputDirectory;
|
|
7178
7287
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -7180,7 +7289,7 @@ var MiddlewareGenerator = class {
|
|
|
7180
7289
|
}
|
|
7181
7290
|
return `@/${importBasePath}/${cleanPath}`;
|
|
7182
7291
|
}
|
|
7183
|
-
|
|
7292
|
+
generate(_context) {
|
|
7184
7293
|
const files = [];
|
|
7185
7294
|
files.push(this.generateMainMiddleware());
|
|
7186
7295
|
files.push(this.generateRateLimitMiddleware());
|
|
@@ -7188,7 +7297,7 @@ var MiddlewareGenerator = class {
|
|
|
7188
7297
|
files.push(this.generateSecurityMiddleware());
|
|
7189
7298
|
files.push(this.generateLoggingMiddleware());
|
|
7190
7299
|
files.push(this.generateApiMiddleware());
|
|
7191
|
-
return files;
|
|
7300
|
+
return Promise.resolve(files);
|
|
7192
7301
|
}
|
|
7193
7302
|
generateMainMiddleware() {
|
|
7194
7303
|
const authImport = this.buildImportPath("auth/middleware");
|
|
@@ -7595,7 +7704,7 @@ var UploadGenerator = class {
|
|
|
7595
7704
|
}
|
|
7596
7705
|
analyzer;
|
|
7597
7706
|
buildImportPath(relativePath) {
|
|
7598
|
-
const outputDirectory = this.configuration.outputDir
|
|
7707
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
7599
7708
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
7600
7709
|
let importBasePath = outputDirectory;
|
|
7601
7710
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -7603,19 +7712,19 @@ var UploadGenerator = class {
|
|
|
7603
7712
|
}
|
|
7604
7713
|
return `@/${importBasePath}/${cleanPath}`;
|
|
7605
7714
|
}
|
|
7606
|
-
|
|
7715
|
+
generate(context) {
|
|
7607
7716
|
const { schema } = context;
|
|
7608
7717
|
const generatedFiles = [];
|
|
7609
7718
|
this.analyzer = new SchemaAnalyzer(schema);
|
|
7610
7719
|
const uploadEndpoints = schema.endpoints.filter(
|
|
7611
|
-
(endpoint) => endpoint.requestBody?.content
|
|
7720
|
+
(endpoint) => endpoint.requestBody?.content.some(
|
|
7612
7721
|
(content) => content.type === "multipart/form-data" || content.type === "application/octet-stream"
|
|
7613
7722
|
)
|
|
7614
7723
|
);
|
|
7615
7724
|
if (uploadEndpoints.length === 0 || !this.configuration.uploads?.enabled) {
|
|
7616
|
-
return generatedFiles;
|
|
7725
|
+
return Promise.resolve(generatedFiles);
|
|
7617
7726
|
}
|
|
7618
|
-
const presignedConfig = this.configuration.uploads
|
|
7727
|
+
const presignedConfig = this.configuration.uploads.presignedUploads;
|
|
7619
7728
|
if (presignedConfig?.enabled) {
|
|
7620
7729
|
const isPresignedSupported = this.analyzer.isPresignedUploadsSupported();
|
|
7621
7730
|
if (!isPresignedSupported) {
|
|
@@ -7626,14 +7735,14 @@ var UploadGenerator = class {
|
|
|
7626
7735
|
}
|
|
7627
7736
|
const extractedUploadConfig = this.analyzer.extractUploadConfig();
|
|
7628
7737
|
const presignedEnabled = presignedConfig?.enabled && this.analyzer.isPresignedUploadsSupported();
|
|
7629
|
-
const uploadProvider = this.configuration.uploads
|
|
7630
|
-
const uploadStrategy = presignedEnabled ? "presigned" : this.configuration.uploads
|
|
7738
|
+
const uploadProvider = this.configuration.uploads.provider || (extractedUploadConfig.presignedEndpoint ? "minio" : "standard");
|
|
7739
|
+
const uploadStrategy = presignedEnabled ? "presigned" : this.configuration.uploads.strategy || "standard";
|
|
7631
7740
|
presignedEnabled ? {
|
|
7632
|
-
presignEndpoint: extractedUploadConfig.presignedEndpoint || presignedConfig
|
|
7633
|
-
fallbackToBackend: presignedConfig
|
|
7741
|
+
presignEndpoint: extractedUploadConfig.presignedEndpoint || presignedConfig.presignEndpoint || "/api/v1/files/presign-upload",
|
|
7742
|
+
fallbackToBackend: presignedConfig.fallbackToBackend !== false
|
|
7634
7743
|
} : {
|
|
7635
7744
|
};
|
|
7636
|
-
const progressConfig = this.configuration.uploads
|
|
7745
|
+
const progressConfig = this.configuration.uploads.progressTracking || {
|
|
7637
7746
|
enabled: true,
|
|
7638
7747
|
useXHR: true
|
|
7639
7748
|
};
|
|
@@ -7650,7 +7759,7 @@ var UploadGenerator = class {
|
|
|
7650
7759
|
progressConfig
|
|
7651
7760
|
));
|
|
7652
7761
|
}
|
|
7653
|
-
return generatedFiles;
|
|
7762
|
+
return Promise.resolve(generatedFiles);
|
|
7654
7763
|
}
|
|
7655
7764
|
/**
|
|
7656
7765
|
* Find presigned upload endpoint in schema
|
|
@@ -7663,7 +7772,7 @@ var UploadGenerator = class {
|
|
|
7663
7772
|
return pathLower.includes("presign") || pathLower.includes("presigned") || operationIdLower.includes("presign") || operationIdLower.includes("presigned");
|
|
7664
7773
|
}) || null;
|
|
7665
7774
|
}
|
|
7666
|
-
generateUploadUtilities(
|
|
7775
|
+
generateUploadUtilities(_uploadProvider, _uploadStrategy, _presignedEndpoint) {
|
|
7667
7776
|
const { uploads } = this.configuration;
|
|
7668
7777
|
const extractedConfig = this.analyzer.extractUploadConfig();
|
|
7669
7778
|
const maxSize = uploads?.security?.maxSize || (extractedConfig.maxSize ? `${extractedConfig.maxSize}MB` : "10MB");
|
|
@@ -8014,8 +8123,8 @@ async function uploadViaBackendApi(
|
|
|
8014
8123
|
xhr.responseType = 'json'
|
|
8015
8124
|
// Use base URL from environment or configuration
|
|
8016
8125
|
const baseUrl = typeof window !== 'undefined'
|
|
8017
|
-
? (process.env.NEXT_PUBLIC_API_URL || '${this.configuration.api
|
|
8018
|
-
: '${this.configuration.api
|
|
8126
|
+
? (process.env.NEXT_PUBLIC_API_URL || '${this.configuration.api.baseUrl || "http://localhost:8000"}')
|
|
8127
|
+
: '${this.configuration.api.baseUrl || "http://localhost:8000"}'
|
|
8019
8128
|
xhr.open('POST', \`\${baseUrl}${endpoint.path}\`)
|
|
8020
8129
|
xhr.send(formData)
|
|
8021
8130
|
})
|
|
@@ -8290,8 +8399,8 @@ export function ${hookName}Upload(options?: {
|
|
|
8290
8399
|
}
|
|
8291
8400
|
generateUseUploadFileHook(uploadProvider, uploadStrategy, context) {
|
|
8292
8401
|
const uploadUtilsImport = this.buildImportPath("services/uploadUtils");
|
|
8293
|
-
const uploadEndpoints = context?.schema
|
|
8294
|
-
(e) => e.metadata
|
|
8402
|
+
const uploadEndpoints = context?.schema.endpoints.filter(
|
|
8403
|
+
(e) => e.metadata.fileUpload || e.path.toLowerCase().includes("upload") || e.operationId && e.operationId.toLowerCase().includes("upload")
|
|
8295
8404
|
) || [];
|
|
8296
8405
|
const firstUploadEndpoint = uploadEndpoints[0];
|
|
8297
8406
|
let uploadMethodName = "uploadFile";
|
|
@@ -8431,7 +8540,7 @@ var ErrorHandlerGenerator = class {
|
|
|
8431
8540
|
}
|
|
8432
8541
|
analyzer;
|
|
8433
8542
|
buildImportPath(relativePath) {
|
|
8434
|
-
const outputDirectory = this.configuration.outputDir
|
|
8543
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
8435
8544
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
8436
8545
|
let importBasePath = outputDirectory;
|
|
8437
8546
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -8439,18 +8548,18 @@ var ErrorHandlerGenerator = class {
|
|
|
8439
8548
|
}
|
|
8440
8549
|
return `@/${importBasePath}/${cleanPath}`;
|
|
8441
8550
|
}
|
|
8442
|
-
|
|
8551
|
+
generate(context) {
|
|
8443
8552
|
const files = [];
|
|
8444
|
-
const { schema
|
|
8553
|
+
const { schema } = context;
|
|
8445
8554
|
const { api } = this.configuration;
|
|
8446
|
-
const errorHandling = api
|
|
8555
|
+
const errorHandling = api.errorHandling;
|
|
8447
8556
|
this.analyzer = new SchemaAnalyzer(schema);
|
|
8448
8557
|
const errorCodes = this.analyzer.extractErrorCodes();
|
|
8449
8558
|
const accountStatusPatterns = this.analyzer.extractAccountStatusPatterns();
|
|
8450
8559
|
const extractedAuthEndpoints = this.analyzer.extractAuthEndpoints();
|
|
8451
8560
|
const hasAuthEndpoints = !!(extractedAuthEndpoints.login || extractedAuthEndpoints.loginCredentials);
|
|
8452
8561
|
const hasAuthErrors = errorCodes.size > 0 || (accountStatusPatterns.inactiveStatuses.length > 0 || accountStatusPatterns.suspendedStatuses.length > 0);
|
|
8453
|
-
const shouldGenerate = hasAuthEndpoints && (errorHandling?.generateAuthErrorHandler !== false && errorHandling?.enableAuthErrorHandling !== false || hasAuthErrors && errorHandling
|
|
8562
|
+
const shouldGenerate = hasAuthEndpoints && (errorHandling?.generateAuthErrorHandler !== false && errorHandling?.enableAuthErrorHandling !== false || hasAuthErrors && errorHandling.generateAuthErrorHandler !== false);
|
|
8454
8563
|
if (shouldGenerate) {
|
|
8455
8564
|
files.push(this.generateAuthErrorHandler(errorCodes, accountStatusPatterns));
|
|
8456
8565
|
} else if (errorHandling?.enableAuthErrorHandling === true && !hasAuthEndpoints) {
|
|
@@ -8458,11 +8567,11 @@ var ErrorHandlerGenerator = class {
|
|
|
8458
8567
|
`\u26A0\uFE0F Auth error handling is enabled but auth endpoints are not found in OpenAPI schema. Auth error handler will not be generated.`
|
|
8459
8568
|
);
|
|
8460
8569
|
}
|
|
8461
|
-
return files;
|
|
8570
|
+
return Promise.resolve(files);
|
|
8462
8571
|
}
|
|
8463
8572
|
generateAuthErrorHandler(errorCodes, accountStatusPatterns) {
|
|
8464
8573
|
const authPath = this.configuration.auth?.authPath || "@/lib/auth";
|
|
8465
|
-
const errorHandling = this.configuration.api
|
|
8574
|
+
const errorHandling = this.configuration.api.errorHandling;
|
|
8466
8575
|
const authErrorHandlerPath = errorHandling?.authErrorHandlerPath || "@/lib/auth-error-handler";
|
|
8467
8576
|
let handlerOutputPath = authErrorHandlerPath;
|
|
8468
8577
|
if (handlerOutputPath.startsWith("@/")) {
|
|
@@ -8501,7 +8610,7 @@ async function getToast(): Promise<{
|
|
|
8501
8610
|
try {
|
|
8502
8611
|
// Try to import sonner (common toast library)
|
|
8503
8612
|
const sonner = await import('sonner')
|
|
8504
|
-
toast = sonner
|
|
8613
|
+
toast = sonner.toast
|
|
8505
8614
|
return toast
|
|
8506
8615
|
} catch {
|
|
8507
8616
|
// Fallback to console if toast library not available
|
|
@@ -8675,7 +8784,7 @@ var SSEGenerator = class {
|
|
|
8675
8784
|
}
|
|
8676
8785
|
analyzer;
|
|
8677
8786
|
buildImportPath(relativePath) {
|
|
8678
|
-
const outputDirectory = this.configuration.outputDir
|
|
8787
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
8679
8788
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
8680
8789
|
let importBasePath = outputDirectory;
|
|
8681
8790
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -8707,11 +8816,11 @@ var SSEGenerator = class {
|
|
|
8707
8816
|
);
|
|
8708
8817
|
return hasEventStream;
|
|
8709
8818
|
}
|
|
8710
|
-
|
|
8819
|
+
generate(context) {
|
|
8711
8820
|
const { schema } = context;
|
|
8712
8821
|
const generatedFiles = [];
|
|
8713
8822
|
this.analyzer = new SchemaAnalyzer(schema);
|
|
8714
|
-
const streamingConfig = this.configuration.framework
|
|
8823
|
+
const streamingConfig = this.configuration.framework.streaming;
|
|
8715
8824
|
const hasStreamingEndpoints = schema.endpoints.some((endpoint) => this.isSSEEndpoint(endpoint));
|
|
8716
8825
|
if (streamingConfig?.enabled === true && !hasStreamingEndpoints) {
|
|
8717
8826
|
console.warn(
|
|
@@ -8720,11 +8829,11 @@ var SSEGenerator = class {
|
|
|
8720
8829
|
}
|
|
8721
8830
|
const shouldEnableStreaming = streamingConfig?.enabled === true ? hasStreamingEndpoints : streamingConfig?.enabled !== false && hasStreamingEndpoints;
|
|
8722
8831
|
if (!shouldEnableStreaming || streamingConfig?.sse?.enabled === false || !hasStreamingEndpoints) {
|
|
8723
|
-
return generatedFiles;
|
|
8832
|
+
return Promise.resolve(generatedFiles);
|
|
8724
8833
|
}
|
|
8725
8834
|
const sseEndpoints = schema.endpoints.filter((endpoint) => this.isSSEEndpoint(endpoint));
|
|
8726
8835
|
if (sseEndpoints.length === 0) {
|
|
8727
|
-
return generatedFiles;
|
|
8836
|
+
return Promise.resolve(generatedFiles);
|
|
8728
8837
|
}
|
|
8729
8838
|
for (const endpoint of sseEndpoints) {
|
|
8730
8839
|
generatedFiles.push(this.generateSSEClientMethod(endpoint));
|
|
@@ -8733,7 +8842,7 @@ var SSEGenerator = class {
|
|
|
8733
8842
|
for (const [tag, tagEndpoints] of Object.entries(endpointsByTag)) {
|
|
8734
8843
|
generatedFiles.push(this.generateSSEHooks(tag, tagEndpoints));
|
|
8735
8844
|
}
|
|
8736
|
-
return generatedFiles;
|
|
8845
|
+
return Promise.resolve(generatedFiles);
|
|
8737
8846
|
}
|
|
8738
8847
|
generateSSEClientMethod(endpoint) {
|
|
8739
8848
|
const operationId = endpoint.operationId || endpoint.id;
|
|
@@ -8768,8 +8877,8 @@ var SSEGenerator = class {
|
|
|
8768
8877
|
}): EventSource => {
|
|
8769
8878
|
${pathParamNames.length > 0 ? `const { ${pathParamNames.join(", ")}, ...restOptions } = options.params || {}
|
|
8770
8879
|
` : ""}const baseUrl = typeof window !== 'undefined'
|
|
8771
|
-
? (process.env.NEXT_PUBLIC_API_URL || '${this.configuration.api
|
|
8772
|
-
: '${this.configuration.api
|
|
8880
|
+
? (process.env.NEXT_PUBLIC_API_URL || '${this.configuration.api.baseUrl || "http://localhost:8000"}')
|
|
8881
|
+
: '${this.configuration.api.baseUrl || "http://localhost:8000"}'
|
|
8773
8882
|
const sseUrl = \`\${baseUrl}${urlConstruction}\`
|
|
8774
8883
|
|
|
8775
8884
|
// Create EventSource connection
|
|
@@ -8815,7 +8924,7 @@ var SSEGenerator = class {
|
|
|
8815
8924
|
generateSSEHooks(tag, endpoints) {
|
|
8816
8925
|
const tagName = toValidIdentifier(tag);
|
|
8817
8926
|
const clientImport = this.buildImportPath("client");
|
|
8818
|
-
const streamingConfig = this.configuration.framework
|
|
8927
|
+
const streamingConfig = this.configuration.framework.streaming?.sse;
|
|
8819
8928
|
const autoReconnect = streamingConfig?.autoReconnect !== false;
|
|
8820
8929
|
const reconnectDelay = streamingConfig?.reconnectDelay || 3e3;
|
|
8821
8930
|
const maxReconnectAttempts = streamingConfig?.maxReconnectAttempts || 5;
|
|
@@ -9008,7 +9117,7 @@ var NextJsCodeGenerator = class {
|
|
|
9008
9117
|
__name(this, "NextJsCodeGenerator");
|
|
9009
9118
|
}
|
|
9010
9119
|
buildImportPath(relativePath) {
|
|
9011
|
-
const outputDirectory = this.configuration.outputDir
|
|
9120
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
9012
9121
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
9013
9122
|
let importBasePath = outputDirectory;
|
|
9014
9123
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -9022,9 +9131,9 @@ var NextJsCodeGenerator = class {
|
|
|
9022
9131
|
path: file.path,
|
|
9023
9132
|
metadata: {
|
|
9024
9133
|
...file.metadata,
|
|
9025
|
-
exports: file.metadata?.exports
|
|
9026
|
-
imports: file.metadata?.imports
|
|
9027
|
-
dependencies: file.metadata?.dependencies
|
|
9134
|
+
exports: file.metadata?.exports ?? [],
|
|
9135
|
+
imports: file.metadata?.imports ?? [],
|
|
9136
|
+
dependencies: file.metadata?.dependencies.map((dependency) => {
|
|
9028
9137
|
if (dependency.startsWith("@/generated/")) {
|
|
9029
9138
|
return dependency;
|
|
9030
9139
|
}
|
|
@@ -9033,7 +9142,7 @@ var NextJsCodeGenerator = class {
|
|
|
9033
9142
|
return `@/generated/${relativePart}`;
|
|
9034
9143
|
}
|
|
9035
9144
|
return dependency;
|
|
9036
|
-
})
|
|
9145
|
+
}) ?? []
|
|
9037
9146
|
}
|
|
9038
9147
|
}));
|
|
9039
9148
|
}
|
|
@@ -9066,11 +9175,11 @@ var NextJsCodeGenerator = class {
|
|
|
9066
9175
|
const uploadGenerator = new UploadGenerator(this.configuration);
|
|
9067
9176
|
generatedFiles.push(...await uploadGenerator.generate(context));
|
|
9068
9177
|
}
|
|
9069
|
-
if (this.configuration.api
|
|
9178
|
+
if (this.configuration.api.errorHandling?.generateAuthErrorHandler !== false) {
|
|
9070
9179
|
const errorHandlerGenerator = new ErrorHandlerGenerator(this.configuration);
|
|
9071
9180
|
generatedFiles.push(...await errorHandlerGenerator.generate(context));
|
|
9072
9181
|
}
|
|
9073
|
-
if (this.configuration.framework
|
|
9182
|
+
if (this.configuration.framework.streaming?.enabled) {
|
|
9074
9183
|
const sseGenerator = new SSEGenerator(this.configuration);
|
|
9075
9184
|
generatedFiles.push(...await sseGenerator.generate(context));
|
|
9076
9185
|
}
|
|
@@ -9080,14 +9189,14 @@ var NextJsCodeGenerator = class {
|
|
|
9080
9189
|
throw new GenerationError(`Failed to generate Next.js files: ${errorMessage}`);
|
|
9081
9190
|
}
|
|
9082
9191
|
}
|
|
9083
|
-
|
|
9192
|
+
generateSafeActionClient() {
|
|
9084
9193
|
const safeActionContent = `import { DEFAULT_SERVER_ERROR_MESSAGE, createSafeActionClient } from "next-safe-action";
|
|
9085
9194
|
import { headers } from "next/headers";
|
|
9086
9195
|
import { z } from "zod";
|
|
9087
9196
|
|
|
9088
9197
|
/**
|
|
9089
9198
|
* Enhanced Action Error class for better error handling
|
|
9090
|
-
* Follows Next.js 16.0.
|
|
9199
|
+
* Follows Next.js 16.0.7 best practices
|
|
9091
9200
|
*/
|
|
9092
9201
|
export class ActionError extends Error {
|
|
9093
9202
|
constructor(
|
|
@@ -9124,7 +9233,7 @@ export const actionClient = createSafeActionClient({
|
|
|
9124
9233
|
|
|
9125
9234
|
/**
|
|
9126
9235
|
* Enhanced action client with metadata support
|
|
9127
|
-
* Next.js 16.0.
|
|
9236
|
+
* Next.js 16.0.7: Supports metadata for better action tracking and rate limiting
|
|
9128
9237
|
*/
|
|
9129
9238
|
export const actionClientWithMeta = createSafeActionClient({
|
|
9130
9239
|
handleServerError(e) {
|
|
@@ -9145,7 +9254,7 @@ export const actionClientWithMeta = createSafeActionClient({
|
|
|
9145
9254
|
requests: z.number().int().positive().describe('Number of requests allowed'),
|
|
9146
9255
|
window: z.string().describe('Time window (e.g., "10s", "1m", "1h")'),
|
|
9147
9256
|
}).optional().describe('Rate limiting configuration'),
|
|
9148
|
-
cacheTags: z.array(z.string()).optional().describe('Cache tags for invalidation (Next.js 16.0.
|
|
9257
|
+
cacheTags: z.array(z.string()).optional().describe('Cache tags for invalidation (Next.js 16.0.7)'),
|
|
9149
9258
|
});
|
|
9150
9259
|
},
|
|
9151
9260
|
});
|
|
@@ -9236,7 +9345,7 @@ async function getCurrentUser() {
|
|
|
9236
9345
|
// Implement your authentication logic here
|
|
9237
9346
|
return null;
|
|
9238
9347
|
}`;
|
|
9239
|
-
return [
|
|
9348
|
+
return Promise.resolve([
|
|
9240
9349
|
{
|
|
9241
9350
|
path: "lib/safe-action.ts",
|
|
9242
9351
|
content: safeActionContent,
|
|
@@ -9247,7 +9356,7 @@ async function getCurrentUser() {
|
|
|
9247
9356
|
dependencies: []
|
|
9248
9357
|
}
|
|
9249
9358
|
}
|
|
9250
|
-
];
|
|
9359
|
+
]);
|
|
9251
9360
|
}
|
|
9252
9361
|
};
|
|
9253
9362
|
var FileSystemManager = class {
|
|
@@ -9255,7 +9364,7 @@ var FileSystemManager = class {
|
|
|
9255
9364
|
__name(this, "FileSystemManager");
|
|
9256
9365
|
}
|
|
9257
9366
|
async writeFile(filePath, content, options = {}) {
|
|
9258
|
-
if (!filePath
|
|
9367
|
+
if (!filePath.trim()) {
|
|
9259
9368
|
throw new Error("File path is required and cannot be empty");
|
|
9260
9369
|
}
|
|
9261
9370
|
if (typeof content !== "string") {
|
|
@@ -9292,7 +9401,7 @@ var FileSystemManager = class {
|
|
|
9292
9401
|
}
|
|
9293
9402
|
}
|
|
9294
9403
|
async readFile(filePath, encoding = "utf8") {
|
|
9295
|
-
if (!filePath
|
|
9404
|
+
if (!filePath.trim()) {
|
|
9296
9405
|
throw new Error("File path is required and cannot be empty");
|
|
9297
9406
|
}
|
|
9298
9407
|
try {
|
|
@@ -9319,7 +9428,7 @@ var FileSystemManager = class {
|
|
|
9319
9428
|
}
|
|
9320
9429
|
}
|
|
9321
9430
|
async getFileHash(filePath) {
|
|
9322
|
-
if (!filePath
|
|
9431
|
+
if (!filePath.trim()) {
|
|
9323
9432
|
throw new Error("File path is required and cannot be empty");
|
|
9324
9433
|
}
|
|
9325
9434
|
try {
|
|
@@ -9345,7 +9454,7 @@ var FileSystemManager = class {
|
|
|
9345
9454
|
}
|
|
9346
9455
|
}
|
|
9347
9456
|
async findFiles(dir, pattern, recursive = true) {
|
|
9348
|
-
if (!dir
|
|
9457
|
+
if (!dir.trim()) {
|
|
9349
9458
|
throw new Error("Directory path is required and cannot be empty");
|
|
9350
9459
|
}
|
|
9351
9460
|
if (!(pattern instanceof RegExp)) {
|
|
@@ -9388,16 +9497,52 @@ var BridgeCore = class {
|
|
|
9388
9497
|
logger;
|
|
9389
9498
|
schemaParser;
|
|
9390
9499
|
fileManager;
|
|
9500
|
+
/**
|
|
9501
|
+
* Creates a new instance of BridgeCore
|
|
9502
|
+
*
|
|
9503
|
+
* Initializes all required dependencies including logger, schema parser, and file manager.
|
|
9504
|
+
* All dependencies are readonly to ensure immutability and thread safety.
|
|
9505
|
+
*/
|
|
9391
9506
|
constructor() {
|
|
9392
9507
|
this.logger = new BridgeLogger({ prefix: "\u{1F517} Mulink" });
|
|
9393
9508
|
this.schemaParser = new OpenApiSchemaParser();
|
|
9394
9509
|
this.fileManager = new FileSystemManager();
|
|
9395
9510
|
}
|
|
9511
|
+
/**
|
|
9512
|
+
* Generates type-safe API client code from OpenAPI schema
|
|
9513
|
+
*
|
|
9514
|
+
* This is the main entry point for code generation. It validates the configuration,
|
|
9515
|
+
* parses the OpenAPI schema, generates all necessary files, and writes them to disk.
|
|
9516
|
+
*
|
|
9517
|
+
* The generated code follows Next.js 16.0.7 best practices including:
|
|
9518
|
+
* - Server Actions with type safety
|
|
9519
|
+
* - React Query hooks with proper caching
|
|
9520
|
+
* - Middleware support
|
|
9521
|
+
* - Error handling
|
|
9522
|
+
* - Cache tag management
|
|
9523
|
+
*
|
|
9524
|
+
* @param configuration - The bridge configuration containing schema URL, output directory, and framework settings
|
|
9525
|
+
* @returns Promise that resolves when generation is complete
|
|
9526
|
+
* @throws {ValidationError} When configuration is invalid
|
|
9527
|
+
* @throws {SchemaParseError} When OpenAPI schema cannot be parsed
|
|
9528
|
+
* @throws {GenerationError} When code generation fails
|
|
9529
|
+
*
|
|
9530
|
+
* @example
|
|
9531
|
+
* ```typescript
|
|
9532
|
+
* const config: BridgeConfiguration = {
|
|
9533
|
+
* schema: 'https://api.example.com/openapi.json',
|
|
9534
|
+
* outputDir: 'src/generated',
|
|
9535
|
+
* framework: { type: 'nextjs', router: 'app', features: {...} },
|
|
9536
|
+
* api: { baseUrl: 'https://api.example.com', timeout: 30000, retries: 3 }
|
|
9537
|
+
* };
|
|
9538
|
+
* await bridge.generate(config);
|
|
9539
|
+
* ```
|
|
9540
|
+
*/
|
|
9396
9541
|
async generate(configuration) {
|
|
9397
9542
|
const startTime = Date.now();
|
|
9398
9543
|
try {
|
|
9399
9544
|
this.logger.info("Starting code generation...");
|
|
9400
|
-
|
|
9545
|
+
this.validateConfiguration(configuration);
|
|
9401
9546
|
this.logger.info(`Parsing schema: ${configuration.schema}`);
|
|
9402
9547
|
const parsedSchema = await this.schemaParser.parse(configuration.schema);
|
|
9403
9548
|
this.logger.debug(
|
|
@@ -9426,16 +9571,17 @@ var BridgeCore = class {
|
|
|
9426
9571
|
throw error;
|
|
9427
9572
|
}
|
|
9428
9573
|
}
|
|
9429
|
-
|
|
9574
|
+
validateConfiguration(configuration) {
|
|
9430
9575
|
const errors = [];
|
|
9431
|
-
if (!configuration.schema
|
|
9576
|
+
if (!configuration.schema.trim()) {
|
|
9432
9577
|
errors.push("Schema URL is required and cannot be empty");
|
|
9433
9578
|
}
|
|
9434
|
-
if (!configuration.outputDir
|
|
9579
|
+
if (!configuration.outputDir.trim()) {
|
|
9435
9580
|
errors.push("Output directory is required and cannot be empty");
|
|
9436
9581
|
}
|
|
9437
9582
|
if (configuration.framework.type !== "nextjs") {
|
|
9438
|
-
|
|
9583
|
+
const frameworkType = configuration.framework.type;
|
|
9584
|
+
errors.push(`Only Next.js framework is currently supported, got: ${frameworkType}`);
|
|
9439
9585
|
}
|
|
9440
9586
|
if (errors.length > 0) {
|
|
9441
9587
|
throw new ValidationError(
|
|
@@ -9452,8 +9598,10 @@ var BridgeCore = class {
|
|
|
9452
9598
|
case "nextjs":
|
|
9453
9599
|
const nextJsGenerator = new NextJsCodeGenerator(config);
|
|
9454
9600
|
return await nextJsGenerator.generate(context);
|
|
9455
|
-
default:
|
|
9456
|
-
|
|
9601
|
+
default: {
|
|
9602
|
+
const frameworkType = config.framework.type;
|
|
9603
|
+
throw new GenerationError(`Unsupported framework: ${frameworkType}`);
|
|
9604
|
+
}
|
|
9457
9605
|
}
|
|
9458
9606
|
} catch (error) {
|
|
9459
9607
|
if (error instanceof GenerationError) {
|
|
@@ -9487,7 +9635,7 @@ var BridgeCore = class {
|
|
|
9487
9635
|
logGenerationSummary(files) {
|
|
9488
9636
|
const fileTypes = files.reduce(
|
|
9489
9637
|
(acc, file) => {
|
|
9490
|
-
acc[file.type] = (acc[file.type]
|
|
9638
|
+
acc[file.type] = (acc[file.type] ?? 0) + 1;
|
|
9491
9639
|
return acc;
|
|
9492
9640
|
},
|
|
9493
9641
|
{}
|
|
@@ -9506,9 +9654,24 @@ var BridgeCore = class {
|
|
|
9506
9654
|
});
|
|
9507
9655
|
}
|
|
9508
9656
|
}
|
|
9509
|
-
|
|
9657
|
+
/**
|
|
9658
|
+
* Validates an OpenAPI schema without generating code
|
|
9659
|
+
*
|
|
9660
|
+
* Useful for checking schema validity before running full generation.
|
|
9661
|
+
*
|
|
9662
|
+
* @param schemaUrl - URL or file path to the OpenAPI schema
|
|
9663
|
+
* @returns Promise resolving to true if schema is valid, false otherwise
|
|
9664
|
+
*
|
|
9665
|
+
* @example
|
|
9666
|
+
* ```typescript
|
|
9667
|
+
* const isValid = await bridge.validateSchema('https://api.example.com/openapi.json');
|
|
9668
|
+
* if (isValid) {
|
|
9669
|
+
* await bridge.generate(config);
|
|
9670
|
+
* }
|
|
9671
|
+
* ```
|
|
9672
|
+
*/
|
|
9510
9673
|
async validateSchema(schemaUrl) {
|
|
9511
|
-
if (!schemaUrl
|
|
9674
|
+
if (!schemaUrl.trim()) {
|
|
9512
9675
|
this.logger.warn("Schema URL is empty or invalid");
|
|
9513
9676
|
return false;
|
|
9514
9677
|
}
|
|
@@ -9522,8 +9685,25 @@ var BridgeCore = class {
|
|
|
9522
9685
|
return false;
|
|
9523
9686
|
}
|
|
9524
9687
|
}
|
|
9688
|
+
/**
|
|
9689
|
+
* Retrieves metadata from an OpenAPI schema
|
|
9690
|
+
*
|
|
9691
|
+
* Extracts schema metadata including title, version, description, and other
|
|
9692
|
+
* OpenAPI document information without parsing the entire schema.
|
|
9693
|
+
*
|
|
9694
|
+
* @param schemaUrl - URL or file path to the OpenAPI schema
|
|
9695
|
+
* @returns Promise resolving to schema metadata
|
|
9696
|
+
* @throws {BridgeError} When schema URL is invalid
|
|
9697
|
+
* @throws {SchemaParseError} When schema cannot be parsed
|
|
9698
|
+
*
|
|
9699
|
+
* @example
|
|
9700
|
+
* ```typescript
|
|
9701
|
+
* const metadata = await bridge.getSchemaInfo('https://api.example.com/openapi.json');
|
|
9702
|
+
* console.log(`Schema: ${metadata.title} v${metadata.version}`);
|
|
9703
|
+
* ```
|
|
9704
|
+
*/
|
|
9525
9705
|
async getSchemaInfo(schemaUrl) {
|
|
9526
|
-
if (!schemaUrl
|
|
9706
|
+
if (!schemaUrl.trim()) {
|
|
9527
9707
|
throw new BridgeError("Schema URL is required and cannot be empty", "INVALID_SCHEMA_URL");
|
|
9528
9708
|
}
|
|
9529
9709
|
try {
|
|
@@ -9535,6 +9715,18 @@ var BridgeCore = class {
|
|
|
9535
9715
|
throw new SchemaParseError(`Failed to parse schema: ${errorMessage}`, schemaUrl);
|
|
9536
9716
|
}
|
|
9537
9717
|
}
|
|
9718
|
+
/**
|
|
9719
|
+
* Clears the internal schema cache
|
|
9720
|
+
*
|
|
9721
|
+
* Useful when you need to force re-fetching of schemas or during development.
|
|
9722
|
+
* The cache is automatically managed, but this method allows manual invalidation.
|
|
9723
|
+
*
|
|
9724
|
+
* @example
|
|
9725
|
+
* ```typescript
|
|
9726
|
+
* bridge.clearCache();
|
|
9727
|
+
* await bridge.generate(config); // Will fetch fresh schema
|
|
9728
|
+
* ```
|
|
9729
|
+
*/
|
|
9538
9730
|
clearCache() {
|
|
9539
9731
|
try {
|
|
9540
9732
|
this.schemaParser.clearCache();
|
|
@@ -9633,7 +9825,7 @@ var ConfigurationLoader = class {
|
|
|
9633
9825
|
}
|
|
9634
9826
|
async load(configPath) {
|
|
9635
9827
|
if (!configPath?.trim()) {
|
|
9636
|
-
configPath = "
|
|
9828
|
+
configPath = "link.config.json";
|
|
9637
9829
|
}
|
|
9638
9830
|
const resolvedPath = path2__default.default.resolve(process.cwd(), configPath);
|
|
9639
9831
|
try {
|
|
@@ -9699,7 +9891,7 @@ ${errorMessages}`,
|
|
|
9699
9891
|
);
|
|
9700
9892
|
}
|
|
9701
9893
|
}
|
|
9702
|
-
|
|
9894
|
+
createDefault(framework = "nextjs") {
|
|
9703
9895
|
const defaultConfig = {
|
|
9704
9896
|
schema: "https://petstore3.swagger.io/api/v3/openapi.json",
|
|
9705
9897
|
outputDir: "src/generated",
|
|
@@ -9744,7 +9936,7 @@ ${errorMessages}`,
|
|
|
9744
9936
|
}
|
|
9745
9937
|
async save(config, configPath) {
|
|
9746
9938
|
if (!configPath?.trim()) {
|
|
9747
|
-
configPath = "
|
|
9939
|
+
configPath = "link.config.json";
|
|
9748
9940
|
}
|
|
9749
9941
|
const resolvedPath = path2__default.default.resolve(process.cwd(), configPath);
|
|
9750
9942
|
try {
|
|
@@ -9802,5 +9994,5 @@ exports.VersionChecker = VersionChecker;
|
|
|
9802
9994
|
exports.__name = __name;
|
|
9803
9995
|
exports.checkAndNotifyUpdates = checkAndNotifyUpdates;
|
|
9804
9996
|
exports.createBridgeVersionChecker = createBridgeVersionChecker;
|
|
9805
|
-
//# sourceMappingURL=chunk-
|
|
9806
|
-
//# sourceMappingURL=chunk-
|
|
9997
|
+
//# sourceMappingURL=chunk-NC747MZJ.cjs.map
|
|
9998
|
+
//# sourceMappingURL=chunk-NC747MZJ.cjs.map
|