mulink 1.1.8 → 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-AHGDKSOY.js → chunk-2WVV6AOO.js} +418 -250
- package/dist/lib/chunk-2WVV6AOO.js.map +1 -0
- package/dist/lib/{chunk-SSMOIVFN.cjs → chunk-NC747MZJ.cjs} +418 -250
- 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-AHGDKSOY.js.map +0 -1
- package/dist/lib/chunk-SSMOIVFN.cjs.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;
|
|
@@ -3703,7 +3778,7 @@ var HookGenerator = class {
|
|
|
3703
3778
|
__name(this, "HookGenerator");
|
|
3704
3779
|
}
|
|
3705
3780
|
buildImportPath(relativePath) {
|
|
3706
|
-
const outputDirectory = this.configuration.outputDir
|
|
3781
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
3707
3782
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
3708
3783
|
let importBasePath = outputDirectory;
|
|
3709
3784
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -3711,7 +3786,7 @@ var HookGenerator = class {
|
|
|
3711
3786
|
}
|
|
3712
3787
|
return `@/${importBasePath}/${cleanPath}`;
|
|
3713
3788
|
}
|
|
3714
|
-
|
|
3789
|
+
generate(context) {
|
|
3715
3790
|
const { schema } = context;
|
|
3716
3791
|
const generatedFiles = [];
|
|
3717
3792
|
const endpointsByTag = this.groupEndpointsByTag(schema.endpoints);
|
|
@@ -3844,7 +3919,7 @@ ${Object.keys(endpointsByTag).map((tag) => `export * from './${toValidIdentifier
|
|
|
3844
3919
|
dependencies: ["./useBridgeQuery", ...Object.keys(endpointsByTag).map((tag) => `./${toValidIdentifier(tag)}`)]
|
|
3845
3920
|
}
|
|
3846
3921
|
});
|
|
3847
|
-
return generatedFiles;
|
|
3922
|
+
return Promise.resolve(generatedFiles);
|
|
3848
3923
|
}
|
|
3849
3924
|
getOperationName(endpoint) {
|
|
3850
3925
|
if (endpoint.operationId) {
|
|
@@ -3861,62 +3936,65 @@ ${Object.keys(endpointsByTag).map((tag) => `export * from './${toValidIdentifier
|
|
|
3861
3936
|
const hookName = toHookName(endpoint.operationId || endpoint.id, true);
|
|
3862
3937
|
const actionName = toActionName(endpoint.operationId || endpoint.id);
|
|
3863
3938
|
const operationName = this.getOperationName(endpoint);
|
|
3864
|
-
const queryKey = this.generateQueryKey(endpoint);
|
|
3865
3939
|
const staleTime = this.getStaleTime(endpoint);
|
|
3866
3940
|
const pathParameters = endpoint.parameters.filter((parameter) => parameter.in === "path");
|
|
3867
3941
|
const queryParameters = endpoint.parameters.filter((parameter) => parameter.in === "query");
|
|
3868
3942
|
const hasPageParam = queryParameters.some((param) => param.name === "page");
|
|
3869
3943
|
const hasSearchParams = endpoint.path.includes("search") || endpoint.path.includes("list");
|
|
3870
|
-
|
|
3871
|
-
const
|
|
3944
|
+
const pathParamTypes = [];
|
|
3945
|
+
const queryParamTypes = [];
|
|
3872
3946
|
pathParameters.forEach((param) => {
|
|
3873
3947
|
const isRequired = param.required ? "" : "?";
|
|
3874
3948
|
const paramType = this.getTypeFromZodSchema(param.schema);
|
|
3875
|
-
|
|
3949
|
+
pathParamTypes.push(`${param.name}${isRequired}: ${paramType}`);
|
|
3876
3950
|
});
|
|
3877
3951
|
queryParameters.forEach((param) => {
|
|
3878
3952
|
const isRequired = param.required ? "" : "?";
|
|
3879
3953
|
const paramType = this.getTypeFromZodSchema(param.schema);
|
|
3880
|
-
|
|
3954
|
+
queryParamTypes.push(`${param.name}${isRequired}: ${paramType}`);
|
|
3881
3955
|
});
|
|
3882
3956
|
const returnType = `z.infer<typeof ${operationName}ResponseSchema>`;
|
|
3883
3957
|
const optionsType = `{ enabled?: boolean; suspense?: boolean; refetchInterval?: number; initialData?: ${returnType} }`;
|
|
3884
|
-
const
|
|
3885
|
-
|
|
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(", ")} }` : "{}";
|
|
3886
3969
|
if (hasSearchParams) {
|
|
3887
|
-
const
|
|
3888
|
-
const isRequired = param.required;
|
|
3889
|
-
const paramType = this.getTypeFromZodSchema(param.schema);
|
|
3890
|
-
return `${param.name}${isRequired ? "" : "?"}: ${paramType}`;
|
|
3891
|
-
}).join("; ");
|
|
3892
|
-
const queryParamsBuild = queryParameters.length > 0 ? `// Build query params object with only the parameters the endpoint expects
|
|
3893
|
-
const queryParams: { ${queryParamTypes} } = {
|
|
3970
|
+
const queryParamsWithFallback = queryParameters.length > 0 ? `const queryParams = {
|
|
3894
3971
|
${queryParameters.map((param) => {
|
|
3895
3972
|
if (param.name === "query") {
|
|
3896
|
-
return `${param.name}:
|
|
3973
|
+
return `${param.name}: params?.query?.${param.name} || searchParams.search || ''`;
|
|
3897
3974
|
} else if (param.name === "limit") {
|
|
3898
|
-
return `${param.name}:
|
|
3975
|
+
return `${param.name}: params?.query?.${param.name} !== undefined ? params.query.${param.name} : searchParams.limit`;
|
|
3899
3976
|
} else {
|
|
3900
|
-
return `${param.name}:
|
|
3977
|
+
return `${param.name}: params?.query?.${param.name}`;
|
|
3901
3978
|
}
|
|
3902
3979
|
}).join(",\n ")}
|
|
3903
|
-
}` : "";
|
|
3904
|
-
const queryParamObject = queryParameters.length > 0 ? `{ ${queryParameters.map((param) => `${param.name}`).join(", ")} }` : "{}";
|
|
3980
|
+
}` : "const queryParams = {}";
|
|
3905
3981
|
return `/**
|
|
3906
3982
|
* Optimized query hook for ${endpoint.method} ${endpoint.path}
|
|
3907
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
|
|
3908
3986
|
* @returns useQuery result with data of type ${returnType}
|
|
3909
3987
|
*/
|
|
3910
|
-
export function ${hookName}(${
|
|
3988
|
+
export function ${hookName}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
3911
3989
|
const [searchParams] = useQueryStates(searchParamsParser)
|
|
3912
3990
|
const { initialData, ...restOptions } = options ?? {}
|
|
3913
3991
|
|
|
3914
3992
|
return useQuery({
|
|
3915
|
-
queryKey: [
|
|
3993
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query, searchParams],
|
|
3916
3994
|
queryFn: async ({ signal }: { signal?: AbortSignal }) => {
|
|
3917
3995
|
try {
|
|
3918
|
-
${
|
|
3919
|
-
const result = await resolveActionResult<${returnType}>(${actionName}(${
|
|
3996
|
+
${queryParamsWithFallback}
|
|
3997
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: queryParams }` : "{ query: queryParams }"}))
|
|
3920
3998
|
return result
|
|
3921
3999
|
} catch (error) {
|
|
3922
4000
|
handleActionError(error)
|
|
@@ -3944,32 +4022,33 @@ export function ${hookName}(${parameterTypes.length > 0 ? `${parameterTypes.join
|
|
|
3944
4022
|
|
|
3945
4023
|
/**
|
|
3946
4024
|
* Infinite query version for paginated ${endpoint.path}
|
|
4025
|
+
* @param params - Named parameters object with path and query parameters
|
|
4026
|
+
* @param options - Query options
|
|
3947
4027
|
* @returns useInfiniteQuery result with data of type ${returnType}
|
|
3948
4028
|
*/
|
|
3949
|
-
export function ${hookName.replace("use", "useInfinite")}(${
|
|
4029
|
+
export function ${hookName.replace("use", "useInfinite")}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
3950
4030
|
const [searchParams] = useQueryStates(searchParamsParser)
|
|
3951
4031
|
const { initialData, ...restOptions } = options ?? {}
|
|
3952
4032
|
|
|
3953
4033
|
return useInfiniteQuery({
|
|
3954
|
-
queryKey: [
|
|
4034
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query, 'infinite', searchParams],
|
|
3955
4035
|
initialPageParam: 1,
|
|
3956
4036
|
queryFn: async ({ pageParam = 1, signal }: { pageParam?: number; signal?: AbortSignal }) => {
|
|
3957
4037
|
try {
|
|
3958
|
-
|
|
3959
|
-
const queryParams: { ${queryParamTypes} } = {
|
|
4038
|
+
const queryParams = {
|
|
3960
4039
|
${queryParameters.map((param) => {
|
|
3961
4040
|
if (param.name === "page" && hasPageParam) {
|
|
3962
4041
|
return `${param.name}: pageParam`;
|
|
3963
4042
|
} else if (param.name === "query") {
|
|
3964
|
-
return `${param.name}:
|
|
4043
|
+
return `${param.name}: params?.query?.${param.name} || searchParams.search || ''`;
|
|
3965
4044
|
} else if (param.name === "limit") {
|
|
3966
|
-
return `${param.name}:
|
|
4045
|
+
return `${param.name}: params?.query?.${param.name} !== undefined ? params.query.${param.name} : searchParams.limit`;
|
|
3967
4046
|
} else {
|
|
3968
|
-
return `${param.name}:
|
|
4047
|
+
return `${param.name}: params?.query?.${param.name}`;
|
|
3969
4048
|
}
|
|
3970
4049
|
}).join(",\n ")}
|
|
3971
4050
|
}
|
|
3972
|
-
const result = await resolveActionResult<${returnType}>(${actionName}(${
|
|
4051
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: queryParams }` : "{ query: queryParams }"}))
|
|
3973
4052
|
return result
|
|
3974
4053
|
} catch (error) {
|
|
3975
4054
|
handleActionError(error)
|
|
@@ -4011,15 +4090,17 @@ export function ${hookName.replace("use", "useInfinite")}(${parameterTypes.lengt
|
|
|
4011
4090
|
|
|
4012
4091
|
/**
|
|
4013
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
|
|
4014
4095
|
* @returns useSuspenseQuery result with data of type ${returnType}
|
|
4015
4096
|
*/
|
|
4016
|
-
export function ${hookName.replace("use", "useSuspense")}(${
|
|
4097
|
+
export function ${hookName.replace("use", "useSuspense")}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
4017
4098
|
const { initialData, ...restOptions } = options ?? {}
|
|
4018
4099
|
|
|
4019
4100
|
return useSuspenseQuery({
|
|
4020
|
-
queryKey: ${
|
|
4101
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query],
|
|
4021
4102
|
queryFn: async () => {
|
|
4022
|
-
const result = await resolveActionResult<${returnType}>(${actionName}(${
|
|
4103
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: ${queryParamsBuild} }` : `{ query: ${queryParamsBuild} }`}))
|
|
4023
4104
|
return result
|
|
4024
4105
|
},
|
|
4025
4106
|
staleTime: ${staleTime},
|
|
@@ -4031,16 +4112,18 @@ export function ${hookName.replace("use", "useSuspense")}(${parameterTypes.lengt
|
|
|
4031
4112
|
return `/**
|
|
4032
4113
|
* Optimized query hook for ${endpoint.method} ${endpoint.path}
|
|
4033
4114
|
* Features: Smart caching, error handling, type safety
|
|
4115
|
+
* @param params - Named parameters object with path and query parameters
|
|
4116
|
+
* @param options - Query options
|
|
4034
4117
|
* @returns useQuery result with data of type ${returnType}
|
|
4035
4118
|
*/
|
|
4036
|
-
export function ${hookName}(${
|
|
4119
|
+
export function ${hookName}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
4037
4120
|
const { initialData, ...restOptions } = options ?? {}
|
|
4038
4121
|
|
|
4039
4122
|
return useQuery({
|
|
4040
|
-
queryKey: ${
|
|
4123
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query],
|
|
4041
4124
|
queryFn: async ({ signal }: { signal?: AbortSignal }) => {
|
|
4042
4125
|
try {
|
|
4043
|
-
const result = await resolveActionResult<${returnType}>(${
|
|
4126
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: ${queryParamsBuild} }` : `{ query: ${queryParamsBuild} }`}))
|
|
4044
4127
|
return result
|
|
4045
4128
|
} catch (error) {
|
|
4046
4129
|
handleActionError(error)
|
|
@@ -4068,15 +4151,17 @@ export function ${hookName}(${parameterTypes.length > 0 ? `${parameterTypes.join
|
|
|
4068
4151
|
|
|
4069
4152
|
/**
|
|
4070
4153
|
* Suspense version for ${endpoint.path}
|
|
4154
|
+
* @param params - Named parameters object with path and query parameters
|
|
4155
|
+
* @param options - Query options
|
|
4071
4156
|
* @returns useSuspenseQuery result with data of type ${returnType}
|
|
4072
4157
|
*/
|
|
4073
|
-
export function ${hookName.replace("use", "useSuspense")}(${
|
|
4158
|
+
export function ${hookName.replace("use", "useSuspense")}(params${pathParameters.length > 0 ? "" : "?"}: ${paramsType}, options?: ${optionsType}) {
|
|
4074
4159
|
const { initialData, ...restOptions } = options ?? {}
|
|
4075
4160
|
|
|
4076
4161
|
return useSuspenseQuery({
|
|
4077
|
-
queryKey: ${
|
|
4162
|
+
queryKey: ['${toActionName(endpoint.operationId || endpoint.id)}', ${pathParameters.length > 0 ? "params?.path" : "null"}, params?.query],
|
|
4078
4163
|
queryFn: async () => {
|
|
4079
|
-
const result = await resolveActionResult<${returnType}>(${
|
|
4164
|
+
const result = await resolveActionResult<${returnType}>(${actionName}(${pathParameters.length > 0 ? `{ path: ${pathParamsBuild}, query: ${queryParamsBuild} }` : `{ query: ${queryParamsBuild} }`}))
|
|
4080
4165
|
return result
|
|
4081
4166
|
},
|
|
4082
4167
|
staleTime: ${staleTime},
|
|
@@ -4191,7 +4276,7 @@ ${invalidationCode}
|
|
|
4191
4276
|
* Find related queries that should be invalidated when this mutation runs
|
|
4192
4277
|
*/
|
|
4193
4278
|
findRelatedQueries(endpoint, context) {
|
|
4194
|
-
if (!context?.schema
|
|
4279
|
+
if (!context?.schema.endpoints) {
|
|
4195
4280
|
return [];
|
|
4196
4281
|
}
|
|
4197
4282
|
const relatedQueries = [];
|
|
@@ -4325,7 +4410,6 @@ ${invalidationCode}
|
|
|
4325
4410
|
buildActionCallParams(endpoint, isMutation) {
|
|
4326
4411
|
const pathParameters = endpoint.parameters.filter((parameter) => parameter.in === "path");
|
|
4327
4412
|
const queryParameters = endpoint.parameters.filter((parameter) => parameter.in === "query");
|
|
4328
|
-
!!endpoint.requestBody;
|
|
4329
4413
|
if (isMutation) {
|
|
4330
4414
|
return "variables";
|
|
4331
4415
|
}
|
|
@@ -4406,13 +4490,13 @@ ${invalidationCode}
|
|
|
4406
4490
|
import { useQuery, useMutation, useInfiniteQuery, useSuspenseQuery, type UseQueryOptions, type UseMutationOptions, type UseInfiniteQueryOptions, type UseSuspenseQueryOptions, type QueryKey, type QueryFunction } from '@tanstack/react-query'
|
|
4407
4491
|
|
|
4408
4492
|
/**
|
|
4409
|
-
* Enhanced React Query wrapper hook with Next.js 16.0.
|
|
4493
|
+
* Enhanced React Query wrapper hook with Next.js 16.0.7 optimizations
|
|
4410
4494
|
* Provides consistent defaults across all queries following best practices
|
|
4411
4495
|
*
|
|
4412
4496
|
* Features:
|
|
4413
4497
|
* - React Query v5: Uses gcTime instead of cacheTime
|
|
4414
4498
|
* - React Query v5: Uses placeholderData instead of keepPreviousData
|
|
4415
|
-
* - Next.js 16.0.
|
|
4499
|
+
* - Next.js 16.0.7: Optimized for App Router and Server Components
|
|
4416
4500
|
*/
|
|
4417
4501
|
export function useBridgeQuery<TData = unknown, TError = Error>(
|
|
4418
4502
|
queryKey: QueryKey,
|
|
@@ -4469,7 +4553,7 @@ export function useBridgeInfiniteQuery<TData = unknown, TError = Error, TPagePar
|
|
|
4469
4553
|
|
|
4470
4554
|
/**
|
|
4471
4555
|
* Enhanced suspense query wrapper
|
|
4472
|
-
* Next.js 16.0.
|
|
4556
|
+
* Next.js 16.0.7: Optimized for Server Components with Suspense
|
|
4473
4557
|
*/
|
|
4474
4558
|
export function useBridgeSuspenseQuery<TData = unknown, TError = Error>(
|
|
4475
4559
|
queryKey: QueryKey,
|
|
@@ -4525,7 +4609,7 @@ var AuthGenerator = class {
|
|
|
4525
4609
|
__name(this, "AuthGenerator");
|
|
4526
4610
|
}
|
|
4527
4611
|
buildImportPath(relativePath) {
|
|
4528
|
-
const outputDirectory = this.configuration.outputDir
|
|
4612
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
4529
4613
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
4530
4614
|
let importBasePath = outputDirectory;
|
|
4531
4615
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -4533,11 +4617,11 @@ var AuthGenerator = class {
|
|
|
4533
4617
|
}
|
|
4534
4618
|
return `@/${importBasePath}/${cleanPath}`;
|
|
4535
4619
|
}
|
|
4536
|
-
|
|
4620
|
+
generate(context) {
|
|
4537
4621
|
const { config, schema } = context;
|
|
4538
4622
|
const authConfig = config.auth;
|
|
4539
4623
|
if (!authConfig?.enabled) {
|
|
4540
|
-
return [];
|
|
4624
|
+
return Promise.resolve([]);
|
|
4541
4625
|
}
|
|
4542
4626
|
const files = [];
|
|
4543
4627
|
const authEndpoints = this.findAuthEndpoints(schema.endpoints);
|
|
@@ -4552,7 +4636,7 @@ var AuthGenerator = class {
|
|
|
4552
4636
|
files.push(...this.generateAuthComponents(authEndpoints));
|
|
4553
4637
|
files.push(this.generateAuthMiddleware());
|
|
4554
4638
|
files.push(this.generateAuthContext());
|
|
4555
|
-
return files;
|
|
4639
|
+
return Promise.resolve(files);
|
|
4556
4640
|
}
|
|
4557
4641
|
findAuthEndpoints(endpoints) {
|
|
4558
4642
|
const authEndpoints = {};
|
|
@@ -4585,12 +4669,12 @@ var AuthGenerator = class {
|
|
|
4585
4669
|
}
|
|
4586
4670
|
return userEndpoints;
|
|
4587
4671
|
}
|
|
4588
|
-
generateAuthSchemas(
|
|
4672
|
+
generateAuthSchemas(_authEndpoints, _schema) {
|
|
4589
4673
|
this.buildImportPath("schemas");
|
|
4590
|
-
const loginRequestSchema =
|
|
4674
|
+
const loginRequestSchema = _authEndpoints.login?.requestBody?.content?.find(
|
|
4591
4675
|
(c) => c.type === "application/json"
|
|
4592
4676
|
)?.schema;
|
|
4593
|
-
const loginResponseSchema =
|
|
4677
|
+
const loginResponseSchema = _authEndpoints.login?.responses?.find(
|
|
4594
4678
|
(r) => r.statusCode === "200"
|
|
4595
4679
|
)?.content?.[0]?.schema;
|
|
4596
4680
|
const content = `import { z } from "zod"
|
|
@@ -4691,7 +4775,7 @@ export type Session = z.infer<typeof sessionSchema>`;
|
|
|
4691
4775
|
}
|
|
4692
4776
|
};
|
|
4693
4777
|
}
|
|
4694
|
-
generateAuthTypes(
|
|
4778
|
+
generateAuthTypes(_authEndpoints, _schema) {
|
|
4695
4779
|
const schemasImport = this.buildImportPath("auth/schemas");
|
|
4696
4780
|
const content = `// Auto-generated authentication types
|
|
4697
4781
|
import type {
|
|
@@ -5237,7 +5321,7 @@ export const useRequireAuth = (redirectTo?: string) => {
|
|
|
5237
5321
|
}
|
|
5238
5322
|
};
|
|
5239
5323
|
}
|
|
5240
|
-
generateAuthActions(
|
|
5324
|
+
generateAuthActions(_authEndpoints, _userEndpoints) {
|
|
5241
5325
|
const safeActionImport = this.buildImportPath("lib/safe-action");
|
|
5242
5326
|
const schemasImport = this.buildImportPath("auth/schemas");
|
|
5243
5327
|
const clientImport = this.buildImportPath("auth/client");
|
|
@@ -5469,7 +5553,7 @@ export const useCredentialsLogin = () => {
|
|
|
5469
5553
|
}
|
|
5470
5554
|
};
|
|
5471
5555
|
}
|
|
5472
|
-
generateAuthComponents(
|
|
5556
|
+
generateAuthComponents(_authEndpoints) {
|
|
5473
5557
|
const loginForm = {
|
|
5474
5558
|
path: "auth/components/login-form.tsx",
|
|
5475
5559
|
content: `"use client"
|
|
@@ -5589,7 +5673,6 @@ export function LoginForm() {
|
|
|
5589
5673
|
return [loginForm];
|
|
5590
5674
|
}
|
|
5591
5675
|
generateAuthMiddleware() {
|
|
5592
|
-
this.buildImportPath("auth/utils");
|
|
5593
5676
|
const content = `import { NextResponse } from "next/server"
|
|
5594
5677
|
import type { NextRequest } from "next/server"
|
|
5595
5678
|
|
|
@@ -5957,9 +6040,10 @@ var SchemaAnalyzer = class {
|
|
|
5957
6040
|
}
|
|
5958
6041
|
if (response.description) {
|
|
5959
6042
|
const errorMatch = response.description.match(/error[_\s]?code[:\s]+(\w+)/i);
|
|
5960
|
-
if (errorMatch) {
|
|
5961
|
-
|
|
5962
|
-
|
|
6043
|
+
if (errorMatch?.[1]) {
|
|
6044
|
+
const errorCode = errorMatch[1];
|
|
6045
|
+
errorCodes.set(errorCode.toLowerCase(), {
|
|
6046
|
+
code: errorCode,
|
|
5963
6047
|
status,
|
|
5964
6048
|
message: response.description
|
|
5965
6049
|
});
|
|
@@ -6045,7 +6129,7 @@ var SchemaAnalyzer = class {
|
|
|
6045
6129
|
const endpoints = {};
|
|
6046
6130
|
for (const endpoint of this.schema.endpoints) {
|
|
6047
6131
|
const path3 = endpoint.path.toLowerCase();
|
|
6048
|
-
const operationId = (endpoint.operationId
|
|
6132
|
+
const operationId = (endpoint.operationId ?? "").toLowerCase();
|
|
6049
6133
|
if ((path3.includes("/auth/login/credentials") || operationId.includes("logincredentials") || operationId.includes("signin")) && endpoint.method === "POST") {
|
|
6050
6134
|
endpoints.loginCredentials = endpoint;
|
|
6051
6135
|
} else if ((path3.includes("/auth/login") || operationId.includes("login")) && endpoint.method === "POST" && !endpoints.loginCredentials) {
|
|
@@ -6093,12 +6177,12 @@ var SchemaAnalyzer = class {
|
|
|
6093
6177
|
extractSessionConfig() {
|
|
6094
6178
|
const config = {};
|
|
6095
6179
|
const sessionEndpoints = this.schema.endpoints.filter(
|
|
6096
|
-
(e) => e.path.toLowerCase().includes("/session") || (e.operationId
|
|
6180
|
+
(e) => e.path.toLowerCase().includes("/session") || (e.operationId ?? "").toLowerCase().includes("session")
|
|
6097
6181
|
);
|
|
6098
6182
|
for (const endpoint of sessionEndpoints) {
|
|
6099
6183
|
for (const response of endpoint.responses) {
|
|
6100
6184
|
if (response.headers) {
|
|
6101
|
-
for (const [headerName,
|
|
6185
|
+
for (const [headerName, _headerDef] of Object.entries(response.headers)) {
|
|
6102
6186
|
if (headerName.toLowerCase().includes("expires") || headerName.toLowerCase().includes("max-age")) ;
|
|
6103
6187
|
}
|
|
6104
6188
|
}
|
|
@@ -6178,7 +6262,7 @@ var SchemaAnalyzer = class {
|
|
|
6178
6262
|
/**
|
|
6179
6263
|
* Check if OAuth provider is supported
|
|
6180
6264
|
*/
|
|
6181
|
-
isOAuthProviderSupported(
|
|
6265
|
+
isOAuthProviderSupported(_provider) {
|
|
6182
6266
|
const extractedEndpoints = this.extractAuthEndpoints();
|
|
6183
6267
|
return !!(extractedEndpoints.oauthAuthorize && extractedEndpoints.oauthCallback);
|
|
6184
6268
|
}
|
|
@@ -6260,7 +6344,7 @@ var SchemaAnalyzer = class {
|
|
|
6260
6344
|
errorCodes.set(code.toLowerCase(), {
|
|
6261
6345
|
code,
|
|
6262
6346
|
status,
|
|
6263
|
-
message: schema.properties?.detail?.default
|
|
6347
|
+
message: schema.properties?.detail?.default ?? schema.properties?.message?.default ?? ""
|
|
6264
6348
|
});
|
|
6265
6349
|
});
|
|
6266
6350
|
}
|
|
@@ -6269,9 +6353,10 @@ var SchemaAnalyzer = class {
|
|
|
6269
6353
|
const detail = schema.properties.detail;
|
|
6270
6354
|
if (typeof detail === "string") {
|
|
6271
6355
|
const codeMatch = detail.match(/(\w+)[_\s]?error/i);
|
|
6272
|
-
if (codeMatch) {
|
|
6273
|
-
|
|
6274
|
-
|
|
6356
|
+
if (codeMatch?.[1]) {
|
|
6357
|
+
const errorCode = codeMatch[1];
|
|
6358
|
+
errorCodes.set(errorCode.toLowerCase(), {
|
|
6359
|
+
code: errorCode,
|
|
6275
6360
|
status,
|
|
6276
6361
|
message: detail
|
|
6277
6362
|
});
|
|
@@ -6283,11 +6368,11 @@ var SchemaAnalyzer = class {
|
|
|
6283
6368
|
if (!schema || typeof schema !== "object") return;
|
|
6284
6369
|
if (schema.properties?.file) {
|
|
6285
6370
|
const fileSchema = schema.properties.file;
|
|
6286
|
-
if (fileSchema.maxSize
|
|
6287
|
-
config.maxSize = fileSchema.maxSize
|
|
6371
|
+
if (fileSchema.maxSize ?? fileSchema["x-max-size"]) {
|
|
6372
|
+
config.maxSize = fileSchema.maxSize ?? fileSchema["x-max-size"];
|
|
6288
6373
|
}
|
|
6289
|
-
if (fileSchema.allowedTypes
|
|
6290
|
-
config.allowedTypes = fileSchema.allowedTypes
|
|
6374
|
+
if (fileSchema.allowedTypes ?? fileSchema["x-allowed-types"]) {
|
|
6375
|
+
config.allowedTypes = fileSchema.allowedTypes ?? fileSchema["x-allowed-types"];
|
|
6291
6376
|
}
|
|
6292
6377
|
}
|
|
6293
6378
|
if (schema["x-file-constraints"]) {
|
|
@@ -6307,7 +6392,7 @@ var NextAuthGenerator = class {
|
|
|
6307
6392
|
analyzer;
|
|
6308
6393
|
accountStatusPatterns;
|
|
6309
6394
|
buildImportPath(relativePath) {
|
|
6310
|
-
const outputDirectory = this.configuration.outputDir
|
|
6395
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
6311
6396
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
6312
6397
|
let importBasePath = outputDirectory;
|
|
6313
6398
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -6315,11 +6400,11 @@ var NextAuthGenerator = class {
|
|
|
6315
6400
|
}
|
|
6316
6401
|
return `@/${importBasePath}/${cleanPath}`;
|
|
6317
6402
|
}
|
|
6318
|
-
|
|
6403
|
+
generate(context) {
|
|
6319
6404
|
const { config, schema } = context;
|
|
6320
6405
|
const authConfig = config.auth;
|
|
6321
6406
|
if (!authConfig?.enabled || authConfig.provider !== "next-auth") {
|
|
6322
|
-
return [];
|
|
6407
|
+
return Promise.resolve([]);
|
|
6323
6408
|
}
|
|
6324
6409
|
this.analyzer = new SchemaAnalyzer(schema);
|
|
6325
6410
|
this.accountStatusPatterns = this.analyzer.extractAccountStatusPatterns();
|
|
@@ -6336,7 +6421,7 @@ var NextAuthGenerator = class {
|
|
|
6336
6421
|
console.warn("These features will be disabled in generated code.\n");
|
|
6337
6422
|
}
|
|
6338
6423
|
const detectedOAuthProviders = this.analyzer.extractOAuthProviders();
|
|
6339
|
-
const configuredOAuthProviders = authConfig.oauth?.providers
|
|
6424
|
+
const configuredOAuthProviders = authConfig.oauth?.providers ?? [];
|
|
6340
6425
|
const validOAuthProviders = configuredOAuthProviders.filter(
|
|
6341
6426
|
(provider) => this.analyzer.isOAuthProviderSupported(provider)
|
|
6342
6427
|
);
|
|
@@ -6347,7 +6432,7 @@ var NextAuthGenerator = class {
|
|
|
6347
6432
|
console.warn(
|
|
6348
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.`
|
|
6349
6434
|
);
|
|
6350
|
-
return files;
|
|
6435
|
+
return Promise.resolve(files);
|
|
6351
6436
|
}
|
|
6352
6437
|
files.push(this.generateNextAuthConfig(
|
|
6353
6438
|
extractedEndpoints,
|
|
@@ -6360,7 +6445,7 @@ var NextAuthGenerator = class {
|
|
|
6360
6445
|
if (hasPasskeys) {
|
|
6361
6446
|
files.push(this.generatePasskeyUtils(extractedEndpoints));
|
|
6362
6447
|
}
|
|
6363
|
-
return files;
|
|
6448
|
+
return Promise.resolve(files);
|
|
6364
6449
|
}
|
|
6365
6450
|
findAuthEndpoints(endpoints) {
|
|
6366
6451
|
const authEndpoints = {};
|
|
@@ -6463,14 +6548,14 @@ var NextAuthGenerator = class {
|
|
|
6463
6548
|
}
|
|
6464
6549
|
generateNextAuthConfig(extractedEndpoints, oauthProviders, hasMFA, hasPasskeys) {
|
|
6465
6550
|
const authConfig = this.configuration.auth;
|
|
6466
|
-
const apiBaseUrl = this.configuration.api
|
|
6551
|
+
const apiBaseUrl = this.configuration.api.baseUrl || "http://localhost:8000";
|
|
6467
6552
|
const apiUrl = `process.env.NEXT_PUBLIC_API_URL || '${apiBaseUrl}'`;
|
|
6468
6553
|
const hasGoogle = oauthProviders.includes("google");
|
|
6469
6554
|
const hasGitHub = oauthProviders.includes("github");
|
|
6470
6555
|
oauthProviders.includes("discord");
|
|
6471
6556
|
oauthProviders.includes("microsoft");
|
|
6472
6557
|
oauthProviders.includes("apple");
|
|
6473
|
-
const { activeStatuses, inactiveStatuses, suspendedStatuses, lockedStatuses } = this.accountStatusPatterns;
|
|
6558
|
+
const { activeStatuses, inactiveStatuses, suspendedStatuses, lockedStatuses: _lockedStatuses } = this.accountStatusPatterns;
|
|
6474
6559
|
this.analyzer.extractErrorCodes();
|
|
6475
6560
|
const sessionStrategy = authConfig.session?.strategy || "jwt";
|
|
6476
6561
|
const sessionMaxAge = authConfig.session?.maxAge || 7 * 24 * 60 * 60;
|
|
@@ -7019,7 +7104,7 @@ export async function authenticateWithPasskey(email?: string) {
|
|
|
7019
7104
|
}
|
|
7020
7105
|
};
|
|
7021
7106
|
}
|
|
7022
|
-
generateAuthTypes(
|
|
7107
|
+
generateAuthTypes(_authEndpoints, _accountStatusPatterns) {
|
|
7023
7108
|
const content = `/**
|
|
7024
7109
|
* Authentication Types
|
|
7025
7110
|
* Auto-generated by Mulink
|
|
@@ -7090,7 +7175,7 @@ export class AccountInactiveError extends AuthError {
|
|
|
7090
7175
|
}
|
|
7091
7176
|
};
|
|
7092
7177
|
}
|
|
7093
|
-
generateAuthUtils(
|
|
7178
|
+
generateAuthUtils(_authEndpoints) {
|
|
7094
7179
|
const content = `/**
|
|
7095
7180
|
* Authentication Utilities
|
|
7096
7181
|
* Auto-generated by Mulink
|
|
@@ -7143,7 +7228,7 @@ export async function requireAuth() {
|
|
|
7143
7228
|
}
|
|
7144
7229
|
};
|
|
7145
7230
|
}
|
|
7146
|
-
generatePasskeyUtils(
|
|
7231
|
+
generatePasskeyUtils(_passkeyEndpoints) {
|
|
7147
7232
|
const content = `/**
|
|
7148
7233
|
* Passkey (WebAuthn) Utilities
|
|
7149
7234
|
* Auto-generated by Mulink
|
|
@@ -7196,7 +7281,7 @@ var MiddlewareGenerator = class {
|
|
|
7196
7281
|
__name(this, "MiddlewareGenerator");
|
|
7197
7282
|
}
|
|
7198
7283
|
buildImportPath(relativePath) {
|
|
7199
|
-
const outputDirectory = this.configuration.outputDir
|
|
7284
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
7200
7285
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
7201
7286
|
let importBasePath = outputDirectory;
|
|
7202
7287
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -7204,7 +7289,7 @@ var MiddlewareGenerator = class {
|
|
|
7204
7289
|
}
|
|
7205
7290
|
return `@/${importBasePath}/${cleanPath}`;
|
|
7206
7291
|
}
|
|
7207
|
-
|
|
7292
|
+
generate(_context) {
|
|
7208
7293
|
const files = [];
|
|
7209
7294
|
files.push(this.generateMainMiddleware());
|
|
7210
7295
|
files.push(this.generateRateLimitMiddleware());
|
|
@@ -7212,7 +7297,7 @@ var MiddlewareGenerator = class {
|
|
|
7212
7297
|
files.push(this.generateSecurityMiddleware());
|
|
7213
7298
|
files.push(this.generateLoggingMiddleware());
|
|
7214
7299
|
files.push(this.generateApiMiddleware());
|
|
7215
|
-
return files;
|
|
7300
|
+
return Promise.resolve(files);
|
|
7216
7301
|
}
|
|
7217
7302
|
generateMainMiddleware() {
|
|
7218
7303
|
const authImport = this.buildImportPath("auth/middleware");
|
|
@@ -7619,7 +7704,7 @@ var UploadGenerator = class {
|
|
|
7619
7704
|
}
|
|
7620
7705
|
analyzer;
|
|
7621
7706
|
buildImportPath(relativePath) {
|
|
7622
|
-
const outputDirectory = this.configuration.outputDir
|
|
7707
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
7623
7708
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
7624
7709
|
let importBasePath = outputDirectory;
|
|
7625
7710
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -7627,19 +7712,19 @@ var UploadGenerator = class {
|
|
|
7627
7712
|
}
|
|
7628
7713
|
return `@/${importBasePath}/${cleanPath}`;
|
|
7629
7714
|
}
|
|
7630
|
-
|
|
7715
|
+
generate(context) {
|
|
7631
7716
|
const { schema } = context;
|
|
7632
7717
|
const generatedFiles = [];
|
|
7633
7718
|
this.analyzer = new SchemaAnalyzer(schema);
|
|
7634
7719
|
const uploadEndpoints = schema.endpoints.filter(
|
|
7635
|
-
(endpoint) => endpoint.requestBody?.content
|
|
7720
|
+
(endpoint) => endpoint.requestBody?.content.some(
|
|
7636
7721
|
(content) => content.type === "multipart/form-data" || content.type === "application/octet-stream"
|
|
7637
7722
|
)
|
|
7638
7723
|
);
|
|
7639
7724
|
if (uploadEndpoints.length === 0 || !this.configuration.uploads?.enabled) {
|
|
7640
|
-
return generatedFiles;
|
|
7725
|
+
return Promise.resolve(generatedFiles);
|
|
7641
7726
|
}
|
|
7642
|
-
const presignedConfig = this.configuration.uploads
|
|
7727
|
+
const presignedConfig = this.configuration.uploads.presignedUploads;
|
|
7643
7728
|
if (presignedConfig?.enabled) {
|
|
7644
7729
|
const isPresignedSupported = this.analyzer.isPresignedUploadsSupported();
|
|
7645
7730
|
if (!isPresignedSupported) {
|
|
@@ -7650,14 +7735,14 @@ var UploadGenerator = class {
|
|
|
7650
7735
|
}
|
|
7651
7736
|
const extractedUploadConfig = this.analyzer.extractUploadConfig();
|
|
7652
7737
|
const presignedEnabled = presignedConfig?.enabled && this.analyzer.isPresignedUploadsSupported();
|
|
7653
|
-
const uploadProvider = this.configuration.uploads
|
|
7654
|
-
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";
|
|
7655
7740
|
presignedEnabled ? {
|
|
7656
|
-
presignEndpoint: extractedUploadConfig.presignedEndpoint || presignedConfig
|
|
7657
|
-
fallbackToBackend: presignedConfig
|
|
7741
|
+
presignEndpoint: extractedUploadConfig.presignedEndpoint || presignedConfig.presignEndpoint || "/api/v1/files/presign-upload",
|
|
7742
|
+
fallbackToBackend: presignedConfig.fallbackToBackend !== false
|
|
7658
7743
|
} : {
|
|
7659
7744
|
};
|
|
7660
|
-
const progressConfig = this.configuration.uploads
|
|
7745
|
+
const progressConfig = this.configuration.uploads.progressTracking || {
|
|
7661
7746
|
enabled: true,
|
|
7662
7747
|
useXHR: true
|
|
7663
7748
|
};
|
|
@@ -7674,7 +7759,7 @@ var UploadGenerator = class {
|
|
|
7674
7759
|
progressConfig
|
|
7675
7760
|
));
|
|
7676
7761
|
}
|
|
7677
|
-
return generatedFiles;
|
|
7762
|
+
return Promise.resolve(generatedFiles);
|
|
7678
7763
|
}
|
|
7679
7764
|
/**
|
|
7680
7765
|
* Find presigned upload endpoint in schema
|
|
@@ -7687,7 +7772,7 @@ var UploadGenerator = class {
|
|
|
7687
7772
|
return pathLower.includes("presign") || pathLower.includes("presigned") || operationIdLower.includes("presign") || operationIdLower.includes("presigned");
|
|
7688
7773
|
}) || null;
|
|
7689
7774
|
}
|
|
7690
|
-
generateUploadUtilities(
|
|
7775
|
+
generateUploadUtilities(_uploadProvider, _uploadStrategy, _presignedEndpoint) {
|
|
7691
7776
|
const { uploads } = this.configuration;
|
|
7692
7777
|
const extractedConfig = this.analyzer.extractUploadConfig();
|
|
7693
7778
|
const maxSize = uploads?.security?.maxSize || (extractedConfig.maxSize ? `${extractedConfig.maxSize}MB` : "10MB");
|
|
@@ -8038,8 +8123,8 @@ async function uploadViaBackendApi(
|
|
|
8038
8123
|
xhr.responseType = 'json'
|
|
8039
8124
|
// Use base URL from environment or configuration
|
|
8040
8125
|
const baseUrl = typeof window !== 'undefined'
|
|
8041
|
-
? (process.env.NEXT_PUBLIC_API_URL || '${this.configuration.api
|
|
8042
|
-
: '${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"}'
|
|
8043
8128
|
xhr.open('POST', \`\${baseUrl}${endpoint.path}\`)
|
|
8044
8129
|
xhr.send(formData)
|
|
8045
8130
|
})
|
|
@@ -8314,8 +8399,8 @@ export function ${hookName}Upload(options?: {
|
|
|
8314
8399
|
}
|
|
8315
8400
|
generateUseUploadFileHook(uploadProvider, uploadStrategy, context) {
|
|
8316
8401
|
const uploadUtilsImport = this.buildImportPath("services/uploadUtils");
|
|
8317
|
-
const uploadEndpoints = context?.schema
|
|
8318
|
-
(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")
|
|
8319
8404
|
) || [];
|
|
8320
8405
|
const firstUploadEndpoint = uploadEndpoints[0];
|
|
8321
8406
|
let uploadMethodName = "uploadFile";
|
|
@@ -8455,7 +8540,7 @@ var ErrorHandlerGenerator = class {
|
|
|
8455
8540
|
}
|
|
8456
8541
|
analyzer;
|
|
8457
8542
|
buildImportPath(relativePath) {
|
|
8458
|
-
const outputDirectory = this.configuration.outputDir
|
|
8543
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
8459
8544
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
8460
8545
|
let importBasePath = outputDirectory;
|
|
8461
8546
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -8463,18 +8548,18 @@ var ErrorHandlerGenerator = class {
|
|
|
8463
8548
|
}
|
|
8464
8549
|
return `@/${importBasePath}/${cleanPath}`;
|
|
8465
8550
|
}
|
|
8466
|
-
|
|
8551
|
+
generate(context) {
|
|
8467
8552
|
const files = [];
|
|
8468
|
-
const { schema
|
|
8553
|
+
const { schema } = context;
|
|
8469
8554
|
const { api } = this.configuration;
|
|
8470
|
-
const errorHandling = api
|
|
8555
|
+
const errorHandling = api.errorHandling;
|
|
8471
8556
|
this.analyzer = new SchemaAnalyzer(schema);
|
|
8472
8557
|
const errorCodes = this.analyzer.extractErrorCodes();
|
|
8473
8558
|
const accountStatusPatterns = this.analyzer.extractAccountStatusPatterns();
|
|
8474
8559
|
const extractedAuthEndpoints = this.analyzer.extractAuthEndpoints();
|
|
8475
8560
|
const hasAuthEndpoints = !!(extractedAuthEndpoints.login || extractedAuthEndpoints.loginCredentials);
|
|
8476
8561
|
const hasAuthErrors = errorCodes.size > 0 || (accountStatusPatterns.inactiveStatuses.length > 0 || accountStatusPatterns.suspendedStatuses.length > 0);
|
|
8477
|
-
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);
|
|
8478
8563
|
if (shouldGenerate) {
|
|
8479
8564
|
files.push(this.generateAuthErrorHandler(errorCodes, accountStatusPatterns));
|
|
8480
8565
|
} else if (errorHandling?.enableAuthErrorHandling === true && !hasAuthEndpoints) {
|
|
@@ -8482,11 +8567,11 @@ var ErrorHandlerGenerator = class {
|
|
|
8482
8567
|
`\u26A0\uFE0F Auth error handling is enabled but auth endpoints are not found in OpenAPI schema. Auth error handler will not be generated.`
|
|
8483
8568
|
);
|
|
8484
8569
|
}
|
|
8485
|
-
return files;
|
|
8570
|
+
return Promise.resolve(files);
|
|
8486
8571
|
}
|
|
8487
8572
|
generateAuthErrorHandler(errorCodes, accountStatusPatterns) {
|
|
8488
8573
|
const authPath = this.configuration.auth?.authPath || "@/lib/auth";
|
|
8489
|
-
const errorHandling = this.configuration.api
|
|
8574
|
+
const errorHandling = this.configuration.api.errorHandling;
|
|
8490
8575
|
const authErrorHandlerPath = errorHandling?.authErrorHandlerPath || "@/lib/auth-error-handler";
|
|
8491
8576
|
let handlerOutputPath = authErrorHandlerPath;
|
|
8492
8577
|
if (handlerOutputPath.startsWith("@/")) {
|
|
@@ -8699,7 +8784,7 @@ var SSEGenerator = class {
|
|
|
8699
8784
|
}
|
|
8700
8785
|
analyzer;
|
|
8701
8786
|
buildImportPath(relativePath) {
|
|
8702
|
-
const outputDirectory = this.configuration.outputDir
|
|
8787
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
8703
8788
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
8704
8789
|
let importBasePath = outputDirectory;
|
|
8705
8790
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -8731,11 +8816,11 @@ var SSEGenerator = class {
|
|
|
8731
8816
|
);
|
|
8732
8817
|
return hasEventStream;
|
|
8733
8818
|
}
|
|
8734
|
-
|
|
8819
|
+
generate(context) {
|
|
8735
8820
|
const { schema } = context;
|
|
8736
8821
|
const generatedFiles = [];
|
|
8737
8822
|
this.analyzer = new SchemaAnalyzer(schema);
|
|
8738
|
-
const streamingConfig = this.configuration.framework
|
|
8823
|
+
const streamingConfig = this.configuration.framework.streaming;
|
|
8739
8824
|
const hasStreamingEndpoints = schema.endpoints.some((endpoint) => this.isSSEEndpoint(endpoint));
|
|
8740
8825
|
if (streamingConfig?.enabled === true && !hasStreamingEndpoints) {
|
|
8741
8826
|
console.warn(
|
|
@@ -8744,11 +8829,11 @@ var SSEGenerator = class {
|
|
|
8744
8829
|
}
|
|
8745
8830
|
const shouldEnableStreaming = streamingConfig?.enabled === true ? hasStreamingEndpoints : streamingConfig?.enabled !== false && hasStreamingEndpoints;
|
|
8746
8831
|
if (!shouldEnableStreaming || streamingConfig?.sse?.enabled === false || !hasStreamingEndpoints) {
|
|
8747
|
-
return generatedFiles;
|
|
8832
|
+
return Promise.resolve(generatedFiles);
|
|
8748
8833
|
}
|
|
8749
8834
|
const sseEndpoints = schema.endpoints.filter((endpoint) => this.isSSEEndpoint(endpoint));
|
|
8750
8835
|
if (sseEndpoints.length === 0) {
|
|
8751
|
-
return generatedFiles;
|
|
8836
|
+
return Promise.resolve(generatedFiles);
|
|
8752
8837
|
}
|
|
8753
8838
|
for (const endpoint of sseEndpoints) {
|
|
8754
8839
|
generatedFiles.push(this.generateSSEClientMethod(endpoint));
|
|
@@ -8757,7 +8842,7 @@ var SSEGenerator = class {
|
|
|
8757
8842
|
for (const [tag, tagEndpoints] of Object.entries(endpointsByTag)) {
|
|
8758
8843
|
generatedFiles.push(this.generateSSEHooks(tag, tagEndpoints));
|
|
8759
8844
|
}
|
|
8760
|
-
return generatedFiles;
|
|
8845
|
+
return Promise.resolve(generatedFiles);
|
|
8761
8846
|
}
|
|
8762
8847
|
generateSSEClientMethod(endpoint) {
|
|
8763
8848
|
const operationId = endpoint.operationId || endpoint.id;
|
|
@@ -8792,8 +8877,8 @@ var SSEGenerator = class {
|
|
|
8792
8877
|
}): EventSource => {
|
|
8793
8878
|
${pathParamNames.length > 0 ? `const { ${pathParamNames.join(", ")}, ...restOptions } = options.params || {}
|
|
8794
8879
|
` : ""}const baseUrl = typeof window !== 'undefined'
|
|
8795
|
-
? (process.env.NEXT_PUBLIC_API_URL || '${this.configuration.api
|
|
8796
|
-
: '${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"}'
|
|
8797
8882
|
const sseUrl = \`\${baseUrl}${urlConstruction}\`
|
|
8798
8883
|
|
|
8799
8884
|
// Create EventSource connection
|
|
@@ -8839,7 +8924,7 @@ var SSEGenerator = class {
|
|
|
8839
8924
|
generateSSEHooks(tag, endpoints) {
|
|
8840
8925
|
const tagName = toValidIdentifier(tag);
|
|
8841
8926
|
const clientImport = this.buildImportPath("client");
|
|
8842
|
-
const streamingConfig = this.configuration.framework
|
|
8927
|
+
const streamingConfig = this.configuration.framework.streaming?.sse;
|
|
8843
8928
|
const autoReconnect = streamingConfig?.autoReconnect !== false;
|
|
8844
8929
|
const reconnectDelay = streamingConfig?.reconnectDelay || 3e3;
|
|
8845
8930
|
const maxReconnectAttempts = streamingConfig?.maxReconnectAttempts || 5;
|
|
@@ -9032,7 +9117,7 @@ var NextJsCodeGenerator = class {
|
|
|
9032
9117
|
__name(this, "NextJsCodeGenerator");
|
|
9033
9118
|
}
|
|
9034
9119
|
buildImportPath(relativePath) {
|
|
9035
|
-
const outputDirectory = this.configuration.outputDir
|
|
9120
|
+
const outputDirectory = this.configuration.outputDir ?? "generated";
|
|
9036
9121
|
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
|
|
9037
9122
|
let importBasePath = outputDirectory;
|
|
9038
9123
|
if (importBasePath.startsWith("src/")) {
|
|
@@ -9046,9 +9131,9 @@ var NextJsCodeGenerator = class {
|
|
|
9046
9131
|
path: file.path,
|
|
9047
9132
|
metadata: {
|
|
9048
9133
|
...file.metadata,
|
|
9049
|
-
exports: file.metadata?.exports
|
|
9050
|
-
imports: file.metadata?.imports
|
|
9051
|
-
dependencies: file.metadata?.dependencies
|
|
9134
|
+
exports: file.metadata?.exports ?? [],
|
|
9135
|
+
imports: file.metadata?.imports ?? [],
|
|
9136
|
+
dependencies: file.metadata?.dependencies.map((dependency) => {
|
|
9052
9137
|
if (dependency.startsWith("@/generated/")) {
|
|
9053
9138
|
return dependency;
|
|
9054
9139
|
}
|
|
@@ -9057,7 +9142,7 @@ var NextJsCodeGenerator = class {
|
|
|
9057
9142
|
return `@/generated/${relativePart}`;
|
|
9058
9143
|
}
|
|
9059
9144
|
return dependency;
|
|
9060
|
-
})
|
|
9145
|
+
}) ?? []
|
|
9061
9146
|
}
|
|
9062
9147
|
}));
|
|
9063
9148
|
}
|
|
@@ -9090,11 +9175,11 @@ var NextJsCodeGenerator = class {
|
|
|
9090
9175
|
const uploadGenerator = new UploadGenerator(this.configuration);
|
|
9091
9176
|
generatedFiles.push(...await uploadGenerator.generate(context));
|
|
9092
9177
|
}
|
|
9093
|
-
if (this.configuration.api
|
|
9178
|
+
if (this.configuration.api.errorHandling?.generateAuthErrorHandler !== false) {
|
|
9094
9179
|
const errorHandlerGenerator = new ErrorHandlerGenerator(this.configuration);
|
|
9095
9180
|
generatedFiles.push(...await errorHandlerGenerator.generate(context));
|
|
9096
9181
|
}
|
|
9097
|
-
if (this.configuration.framework
|
|
9182
|
+
if (this.configuration.framework.streaming?.enabled) {
|
|
9098
9183
|
const sseGenerator = new SSEGenerator(this.configuration);
|
|
9099
9184
|
generatedFiles.push(...await sseGenerator.generate(context));
|
|
9100
9185
|
}
|
|
@@ -9104,14 +9189,14 @@ var NextJsCodeGenerator = class {
|
|
|
9104
9189
|
throw new GenerationError(`Failed to generate Next.js files: ${errorMessage}`);
|
|
9105
9190
|
}
|
|
9106
9191
|
}
|
|
9107
|
-
|
|
9192
|
+
generateSafeActionClient() {
|
|
9108
9193
|
const safeActionContent = `import { DEFAULT_SERVER_ERROR_MESSAGE, createSafeActionClient } from "next-safe-action";
|
|
9109
9194
|
import { headers } from "next/headers";
|
|
9110
9195
|
import { z } from "zod";
|
|
9111
9196
|
|
|
9112
9197
|
/**
|
|
9113
9198
|
* Enhanced Action Error class for better error handling
|
|
9114
|
-
* Follows Next.js 16.0.
|
|
9199
|
+
* Follows Next.js 16.0.7 best practices
|
|
9115
9200
|
*/
|
|
9116
9201
|
export class ActionError extends Error {
|
|
9117
9202
|
constructor(
|
|
@@ -9148,7 +9233,7 @@ export const actionClient = createSafeActionClient({
|
|
|
9148
9233
|
|
|
9149
9234
|
/**
|
|
9150
9235
|
* Enhanced action client with metadata support
|
|
9151
|
-
* Next.js 16.0.
|
|
9236
|
+
* Next.js 16.0.7: Supports metadata for better action tracking and rate limiting
|
|
9152
9237
|
*/
|
|
9153
9238
|
export const actionClientWithMeta = createSafeActionClient({
|
|
9154
9239
|
handleServerError(e) {
|
|
@@ -9169,7 +9254,7 @@ export const actionClientWithMeta = createSafeActionClient({
|
|
|
9169
9254
|
requests: z.number().int().positive().describe('Number of requests allowed'),
|
|
9170
9255
|
window: z.string().describe('Time window (e.g., "10s", "1m", "1h")'),
|
|
9171
9256
|
}).optional().describe('Rate limiting configuration'),
|
|
9172
|
-
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)'),
|
|
9173
9258
|
});
|
|
9174
9259
|
},
|
|
9175
9260
|
});
|
|
@@ -9260,7 +9345,7 @@ async function getCurrentUser() {
|
|
|
9260
9345
|
// Implement your authentication logic here
|
|
9261
9346
|
return null;
|
|
9262
9347
|
}`;
|
|
9263
|
-
return [
|
|
9348
|
+
return Promise.resolve([
|
|
9264
9349
|
{
|
|
9265
9350
|
path: "lib/safe-action.ts",
|
|
9266
9351
|
content: safeActionContent,
|
|
@@ -9271,7 +9356,7 @@ async function getCurrentUser() {
|
|
|
9271
9356
|
dependencies: []
|
|
9272
9357
|
}
|
|
9273
9358
|
}
|
|
9274
|
-
];
|
|
9359
|
+
]);
|
|
9275
9360
|
}
|
|
9276
9361
|
};
|
|
9277
9362
|
var FileSystemManager = class {
|
|
@@ -9279,7 +9364,7 @@ var FileSystemManager = class {
|
|
|
9279
9364
|
__name(this, "FileSystemManager");
|
|
9280
9365
|
}
|
|
9281
9366
|
async writeFile(filePath, content, options = {}) {
|
|
9282
|
-
if (!filePath
|
|
9367
|
+
if (!filePath.trim()) {
|
|
9283
9368
|
throw new Error("File path is required and cannot be empty");
|
|
9284
9369
|
}
|
|
9285
9370
|
if (typeof content !== "string") {
|
|
@@ -9316,7 +9401,7 @@ var FileSystemManager = class {
|
|
|
9316
9401
|
}
|
|
9317
9402
|
}
|
|
9318
9403
|
async readFile(filePath, encoding = "utf8") {
|
|
9319
|
-
if (!filePath
|
|
9404
|
+
if (!filePath.trim()) {
|
|
9320
9405
|
throw new Error("File path is required and cannot be empty");
|
|
9321
9406
|
}
|
|
9322
9407
|
try {
|
|
@@ -9343,7 +9428,7 @@ var FileSystemManager = class {
|
|
|
9343
9428
|
}
|
|
9344
9429
|
}
|
|
9345
9430
|
async getFileHash(filePath) {
|
|
9346
|
-
if (!filePath
|
|
9431
|
+
if (!filePath.trim()) {
|
|
9347
9432
|
throw new Error("File path is required and cannot be empty");
|
|
9348
9433
|
}
|
|
9349
9434
|
try {
|
|
@@ -9369,7 +9454,7 @@ var FileSystemManager = class {
|
|
|
9369
9454
|
}
|
|
9370
9455
|
}
|
|
9371
9456
|
async findFiles(dir, pattern, recursive = true) {
|
|
9372
|
-
if (!dir
|
|
9457
|
+
if (!dir.trim()) {
|
|
9373
9458
|
throw new Error("Directory path is required and cannot be empty");
|
|
9374
9459
|
}
|
|
9375
9460
|
if (!(pattern instanceof RegExp)) {
|
|
@@ -9412,16 +9497,52 @@ var BridgeCore = class {
|
|
|
9412
9497
|
logger;
|
|
9413
9498
|
schemaParser;
|
|
9414
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
|
+
*/
|
|
9415
9506
|
constructor() {
|
|
9416
9507
|
this.logger = new BridgeLogger({ prefix: "\u{1F517} Mulink" });
|
|
9417
9508
|
this.schemaParser = new OpenApiSchemaParser();
|
|
9418
9509
|
this.fileManager = new FileSystemManager();
|
|
9419
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
|
+
*/
|
|
9420
9541
|
async generate(configuration) {
|
|
9421
9542
|
const startTime = Date.now();
|
|
9422
9543
|
try {
|
|
9423
9544
|
this.logger.info("Starting code generation...");
|
|
9424
|
-
|
|
9545
|
+
this.validateConfiguration(configuration);
|
|
9425
9546
|
this.logger.info(`Parsing schema: ${configuration.schema}`);
|
|
9426
9547
|
const parsedSchema = await this.schemaParser.parse(configuration.schema);
|
|
9427
9548
|
this.logger.debug(
|
|
@@ -9450,16 +9571,17 @@ var BridgeCore = class {
|
|
|
9450
9571
|
throw error;
|
|
9451
9572
|
}
|
|
9452
9573
|
}
|
|
9453
|
-
|
|
9574
|
+
validateConfiguration(configuration) {
|
|
9454
9575
|
const errors = [];
|
|
9455
|
-
if (!configuration.schema
|
|
9576
|
+
if (!configuration.schema.trim()) {
|
|
9456
9577
|
errors.push("Schema URL is required and cannot be empty");
|
|
9457
9578
|
}
|
|
9458
|
-
if (!configuration.outputDir
|
|
9579
|
+
if (!configuration.outputDir.trim()) {
|
|
9459
9580
|
errors.push("Output directory is required and cannot be empty");
|
|
9460
9581
|
}
|
|
9461
9582
|
if (configuration.framework.type !== "nextjs") {
|
|
9462
|
-
|
|
9583
|
+
const frameworkType = configuration.framework.type;
|
|
9584
|
+
errors.push(`Only Next.js framework is currently supported, got: ${frameworkType}`);
|
|
9463
9585
|
}
|
|
9464
9586
|
if (errors.length > 0) {
|
|
9465
9587
|
throw new ValidationError(
|
|
@@ -9476,8 +9598,10 @@ var BridgeCore = class {
|
|
|
9476
9598
|
case "nextjs":
|
|
9477
9599
|
const nextJsGenerator = new NextJsCodeGenerator(config);
|
|
9478
9600
|
return await nextJsGenerator.generate(context);
|
|
9479
|
-
default:
|
|
9480
|
-
|
|
9601
|
+
default: {
|
|
9602
|
+
const frameworkType = config.framework.type;
|
|
9603
|
+
throw new GenerationError(`Unsupported framework: ${frameworkType}`);
|
|
9604
|
+
}
|
|
9481
9605
|
}
|
|
9482
9606
|
} catch (error) {
|
|
9483
9607
|
if (error instanceof GenerationError) {
|
|
@@ -9511,7 +9635,7 @@ var BridgeCore = class {
|
|
|
9511
9635
|
logGenerationSummary(files) {
|
|
9512
9636
|
const fileTypes = files.reduce(
|
|
9513
9637
|
(acc, file) => {
|
|
9514
|
-
acc[file.type] = (acc[file.type]
|
|
9638
|
+
acc[file.type] = (acc[file.type] ?? 0) + 1;
|
|
9515
9639
|
return acc;
|
|
9516
9640
|
},
|
|
9517
9641
|
{}
|
|
@@ -9530,9 +9654,24 @@ var BridgeCore = class {
|
|
|
9530
9654
|
});
|
|
9531
9655
|
}
|
|
9532
9656
|
}
|
|
9533
|
-
|
|
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
|
+
*/
|
|
9534
9673
|
async validateSchema(schemaUrl) {
|
|
9535
|
-
if (!schemaUrl
|
|
9674
|
+
if (!schemaUrl.trim()) {
|
|
9536
9675
|
this.logger.warn("Schema URL is empty or invalid");
|
|
9537
9676
|
return false;
|
|
9538
9677
|
}
|
|
@@ -9546,8 +9685,25 @@ var BridgeCore = class {
|
|
|
9546
9685
|
return false;
|
|
9547
9686
|
}
|
|
9548
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
|
+
*/
|
|
9549
9705
|
async getSchemaInfo(schemaUrl) {
|
|
9550
|
-
if (!schemaUrl
|
|
9706
|
+
if (!schemaUrl.trim()) {
|
|
9551
9707
|
throw new BridgeError("Schema URL is required and cannot be empty", "INVALID_SCHEMA_URL");
|
|
9552
9708
|
}
|
|
9553
9709
|
try {
|
|
@@ -9559,6 +9715,18 @@ var BridgeCore = class {
|
|
|
9559
9715
|
throw new SchemaParseError(`Failed to parse schema: ${errorMessage}`, schemaUrl);
|
|
9560
9716
|
}
|
|
9561
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
|
+
*/
|
|
9562
9730
|
clearCache() {
|
|
9563
9731
|
try {
|
|
9564
9732
|
this.schemaParser.clearCache();
|
|
@@ -9657,7 +9825,7 @@ var ConfigurationLoader = class {
|
|
|
9657
9825
|
}
|
|
9658
9826
|
async load(configPath) {
|
|
9659
9827
|
if (!configPath?.trim()) {
|
|
9660
|
-
configPath = "
|
|
9828
|
+
configPath = "link.config.json";
|
|
9661
9829
|
}
|
|
9662
9830
|
const resolvedPath = path2__default.default.resolve(process.cwd(), configPath);
|
|
9663
9831
|
try {
|
|
@@ -9723,7 +9891,7 @@ ${errorMessages}`,
|
|
|
9723
9891
|
);
|
|
9724
9892
|
}
|
|
9725
9893
|
}
|
|
9726
|
-
|
|
9894
|
+
createDefault(framework = "nextjs") {
|
|
9727
9895
|
const defaultConfig = {
|
|
9728
9896
|
schema: "https://petstore3.swagger.io/api/v3/openapi.json",
|
|
9729
9897
|
outputDir: "src/generated",
|
|
@@ -9768,7 +9936,7 @@ ${errorMessages}`,
|
|
|
9768
9936
|
}
|
|
9769
9937
|
async save(config, configPath) {
|
|
9770
9938
|
if (!configPath?.trim()) {
|
|
9771
|
-
configPath = "
|
|
9939
|
+
configPath = "link.config.json";
|
|
9772
9940
|
}
|
|
9773
9941
|
const resolvedPath = path2__default.default.resolve(process.cwd(), configPath);
|
|
9774
9942
|
try {
|
|
@@ -9826,5 +9994,5 @@ exports.VersionChecker = VersionChecker;
|
|
|
9826
9994
|
exports.__name = __name;
|
|
9827
9995
|
exports.checkAndNotifyUpdates = checkAndNotifyUpdates;
|
|
9828
9996
|
exports.createBridgeVersionChecker = createBridgeVersionChecker;
|
|
9829
|
-
//# sourceMappingURL=chunk-
|
|
9830
|
-
//# sourceMappingURL=chunk-
|
|
9997
|
+
//# sourceMappingURL=chunk-NC747MZJ.cjs.map
|
|
9998
|
+
//# sourceMappingURL=chunk-NC747MZJ.cjs.map
|