@open-mercato/shared 0.4.2-canary-c02407ff85
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/build.mjs +101 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +7 -0
- package/dist/lib/api/crud.js +47 -0
- package/dist/lib/api/crud.js.map +7 -0
- package/dist/lib/api/scoped.js +140 -0
- package/dist/lib/api/scoped.js.map +7 -0
- package/dist/lib/auth/jwt.js +34 -0
- package/dist/lib/auth/jwt.js.map +7 -0
- package/dist/lib/auth/server.js +157 -0
- package/dist/lib/auth/server.js.map +7 -0
- package/dist/lib/boolean.js +22 -0
- package/dist/lib/boolean.js.map +7 -0
- package/dist/lib/bootstrap/appResolver.js +43 -0
- package/dist/lib/bootstrap/appResolver.js.map +7 -0
- package/dist/lib/bootstrap/dynamicLoader.js +108 -0
- package/dist/lib/bootstrap/dynamicLoader.js.map +7 -0
- package/dist/lib/bootstrap/factory.js +59 -0
- package/dist/lib/bootstrap/factory.js.map +7 -0
- package/dist/lib/bootstrap/index.js +11 -0
- package/dist/lib/bootstrap/index.js.map +7 -0
- package/dist/lib/bootstrap/types.js +1 -0
- package/dist/lib/bootstrap/types.js.map +7 -0
- package/dist/lib/cache/segments.js +36 -0
- package/dist/lib/cache/segments.js.map +7 -0
- package/dist/lib/cli/progress.js +46 -0
- package/dist/lib/cli/progress.js.map +7 -0
- package/dist/lib/commands/command-bus.js +285 -0
- package/dist/lib/commands/command-bus.js.map +7 -0
- package/dist/lib/commands/customFieldSnapshots.js +66 -0
- package/dist/lib/commands/customFieldSnapshots.js.map +7 -0
- package/dist/lib/commands/helpers.js +98 -0
- package/dist/lib/commands/helpers.js.map +7 -0
- package/dist/lib/commands/index.js +8 -0
- package/dist/lib/commands/index.js.map +7 -0
- package/dist/lib/commands/operationMetadata.js +32 -0
- package/dist/lib/commands/operationMetadata.js.map +7 -0
- package/dist/lib/commands/registry.js +43 -0
- package/dist/lib/commands/registry.js.map +7 -0
- package/dist/lib/commands/scope.js +44 -0
- package/dist/lib/commands/scope.js.map +7 -0
- package/dist/lib/commands/types.js +8 -0
- package/dist/lib/commands/types.js.map +7 -0
- package/dist/lib/crud/cache-stats.js +98 -0
- package/dist/lib/crud/cache-stats.js.map +7 -0
- package/dist/lib/crud/cache.js +175 -0
- package/dist/lib/crud/cache.js.map +7 -0
- package/dist/lib/crud/custom-fields-client.js +52 -0
- package/dist/lib/crud/custom-fields-client.js.map +7 -0
- package/dist/lib/crud/custom-fields.js +467 -0
- package/dist/lib/crud/custom-fields.js.map +7 -0
- package/dist/lib/crud/errors.js +24 -0
- package/dist/lib/crud/errors.js.map +7 -0
- package/dist/lib/crud/exporters.js +154 -0
- package/dist/lib/crud/exporters.js.map +7 -0
- package/dist/lib/crud/factory.js +1311 -0
- package/dist/lib/crud/factory.js.map +7 -0
- package/dist/lib/crud/types.js +1 -0
- package/dist/lib/crud/types.js.map +7 -0
- package/dist/lib/custom-fields/normalize.js +36 -0
- package/dist/lib/custom-fields/normalize.js.map +7 -0
- package/dist/lib/data/engine.js +396 -0
- package/dist/lib/data/engine.js.map +7 -0
- package/dist/lib/db/escapeLikePattern.js +5 -0
- package/dist/lib/db/escapeLikePattern.js.map +7 -0
- package/dist/lib/db/mikro.js +82 -0
- package/dist/lib/db/mikro.js.map +7 -0
- package/dist/lib/di/container.js +94 -0
- package/dist/lib/di/container.js.map +7 -0
- package/dist/lib/email/send.js +12 -0
- package/dist/lib/email/send.js.map +7 -0
- package/dist/lib/encryption/aes.js +58 -0
- package/dist/lib/encryption/aes.js.map +7 -0
- package/dist/lib/encryption/customFieldValues.js +49 -0
- package/dist/lib/encryption/customFieldValues.js.map +7 -0
- package/dist/lib/encryption/entityFields.js +26 -0
- package/dist/lib/encryption/entityFields.js.map +7 -0
- package/dist/lib/encryption/entityIds.js +80 -0
- package/dist/lib/encryption/entityIds.js.map +7 -0
- package/dist/lib/encryption/find.js +45 -0
- package/dist/lib/encryption/find.js.map +7 -0
- package/dist/lib/encryption/indexDoc.js +69 -0
- package/dist/lib/encryption/indexDoc.js.map +7 -0
- package/dist/lib/encryption/kms.js +282 -0
- package/dist/lib/encryption/kms.js.map +7 -0
- package/dist/lib/encryption/subscriber.js +330 -0
- package/dist/lib/encryption/subscriber.js.map +7 -0
- package/dist/lib/encryption/tenantDataEncryptionService.js +252 -0
- package/dist/lib/encryption/tenantDataEncryptionService.js.map +7 -0
- package/dist/lib/encryption/toggles.js +18 -0
- package/dist/lib/encryption/toggles.js.map +7 -0
- package/dist/lib/entities/naming.js +9 -0
- package/dist/lib/entities/naming.js.map +7 -0
- package/dist/lib/entities/system-entities.js +43 -0
- package/dist/lib/entities/system-entities.js.map +7 -0
- package/dist/lib/frontend/organizationEvents.js +41 -0
- package/dist/lib/frontend/organizationEvents.js.map +7 -0
- package/dist/lib/frontend/useOrganizationScope.js +32 -0
- package/dist/lib/frontend/useOrganizationScope.js.map +7 -0
- package/dist/lib/hotkeys/index.js +128 -0
- package/dist/lib/hotkeys/index.js.map +7 -0
- package/dist/lib/i18n/app-dictionaries.js +17 -0
- package/dist/lib/i18n/app-dictionaries.js.map +7 -0
- package/dist/lib/i18n/config.js +7 -0
- package/dist/lib/i18n/config.js.map +7 -0
- package/dist/lib/i18n/context.js +50 -0
- package/dist/lib/i18n/context.js.map +7 -0
- package/dist/lib/i18n/server.js +68 -0
- package/dist/lib/i18n/server.js.map +7 -0
- package/dist/lib/i18n/translate.js +45 -0
- package/dist/lib/i18n/translate.js.map +7 -0
- package/dist/lib/indexers/error-log.js +82 -0
- package/dist/lib/indexers/error-log.js.map +7 -0
- package/dist/lib/indexers/status-log.js +80 -0
- package/dist/lib/indexers/status-log.js.map +7 -0
- package/dist/lib/lib/auth/jwt.js +34 -0
- package/dist/lib/lib/auth/jwt.js.map +7 -0
- package/dist/lib/lib/auth/server.js +77 -0
- package/dist/lib/lib/auth/server.js.map +7 -0
- package/dist/lib/lib/email/send.js +12 -0
- package/dist/lib/lib/email/send.js.map +7 -0
- package/dist/lib/lib/i18n/config.js +7 -0
- package/dist/lib/lib/i18n/config.js.map +7 -0
- package/dist/lib/lib/i18n/context.js +31 -0
- package/dist/lib/lib/i18n/context.js.map +7 -0
- package/dist/lib/lib/utils.js +9 -0
- package/dist/lib/lib/utils.js.map +7 -0
- package/dist/lib/location/countries.js +68 -0
- package/dist/lib/location/countries.js.map +7 -0
- package/dist/lib/modules/index.js +6 -0
- package/dist/lib/modules/index.js.map +7 -0
- package/dist/lib/modules/registry.js +18 -0
- package/dist/lib/modules/registry.js.map +7 -0
- package/dist/lib/openapi/crud.js +137 -0
- package/dist/lib/openapi/crud.js.map +7 -0
- package/dist/lib/openapi/generator.js +1131 -0
- package/dist/lib/openapi/generator.js.map +7 -0
- package/dist/lib/openapi/index.js +10 -0
- package/dist/lib/openapi/index.js.map +7 -0
- package/dist/lib/openapi/sanitize.js +110 -0
- package/dist/lib/openapi/sanitize.js.map +7 -0
- package/dist/lib/openapi/types.js +1 -0
- package/dist/lib/openapi/types.js.map +7 -0
- package/dist/lib/profiler/index.js +258 -0
- package/dist/lib/profiler/index.js.map +7 -0
- package/dist/lib/query/engine.js +729 -0
- package/dist/lib/query/engine.js.map +7 -0
- package/dist/lib/query/join-utils.js +195 -0
- package/dist/lib/query/join-utils.js.map +7 -0
- package/dist/lib/query/types.js +9 -0
- package/dist/lib/query/types.js.map +7 -0
- package/dist/lib/search/config.js +32 -0
- package/dist/lib/search/config.js.map +7 -0
- package/dist/lib/search/tokenize.js +34 -0
- package/dist/lib/search/tokenize.js.map +7 -0
- package/dist/lib/slugify.js +24 -0
- package/dist/lib/slugify.js.map +7 -0
- package/dist/lib/testing/bootstrap.js +51 -0
- package/dist/lib/testing/bootstrap.js.map +7 -0
- package/dist/lib/testing/index.js +17 -0
- package/dist/lib/testing/index.js.map +7 -0
- package/dist/lib/testing/renderWithProviders.js +15 -0
- package/dist/lib/testing/renderWithProviders.js.map +7 -0
- package/dist/lib/url.js +12 -0
- package/dist/lib/url.js.map +7 -0
- package/dist/lib/utils.js +13 -0
- package/dist/lib/utils.js.map +7 -0
- package/dist/lib/version.js +7 -0
- package/dist/lib/version.js.map +7 -0
- package/dist/modules/dashboard/widgets.js +1 -0
- package/dist/modules/dashboard/widgets.js.map +7 -0
- package/dist/modules/dsl.js +30 -0
- package/dist/modules/dsl.js.map +7 -0
- package/dist/modules/entities/kinds.js +22 -0
- package/dist/modules/entities/kinds.js.map +7 -0
- package/dist/modules/entities/options.js +26 -0
- package/dist/modules/entities/options.js.map +7 -0
- package/dist/modules/entities/validation.js +102 -0
- package/dist/modules/entities/validation.js.map +7 -0
- package/dist/modules/entities/validators.js +88 -0
- package/dist/modules/entities/validators.js.map +7 -0
- package/dist/modules/entities.js +1 -0
- package/dist/modules/entities.js.map +7 -0
- package/dist/modules/navigation/sidebarPreferences.js +50 -0
- package/dist/modules/navigation/sidebarPreferences.js.map +7 -0
- package/dist/modules/perspectives/types.js +1 -0
- package/dist/modules/perspectives/types.js.map +7 -0
- package/dist/modules/registry.js +96 -0
- package/dist/modules/registry.js.map +7 -0
- package/dist/modules/search.js +15 -0
- package/dist/modules/search.js.map +7 -0
- package/dist/modules/vector.js +1 -0
- package/dist/modules/vector.js.map +7 -0
- package/dist/modules/widgets/injection-loader.js +180 -0
- package/dist/modules/widgets/injection-loader.js.map +7 -0
- package/dist/modules/widgets/injection.js +1 -0
- package/dist/modules/widgets/injection.js.map +7 -0
- package/dist/security/features.js +23 -0
- package/dist/security/features.js.map +7 -0
- package/dist/types/pg.d.js +1 -0
- package/dist/types/pg.d.js.map +7 -0
- package/dist/types/react-email.d.js +1 -0
- package/dist/types/react-email.d.js.map +7 -0
- package/dist/types/resend.d.js +1 -0
- package/dist/types/resend.d.js.map +7 -0
- package/jest.config.cjs +22 -0
- package/package.json +88 -0
- package/src/index.ts +0 -0
- package/src/lib/api/__tests__/scoped.test.ts +38 -0
- package/src/lib/api/crud.ts +59 -0
- package/src/lib/api/scoped.ts +239 -0
- package/src/lib/auth/jwt.ts +39 -0
- package/src/lib/auth/server.ts +199 -0
- package/src/lib/boolean.ts +17 -0
- package/src/lib/bootstrap/appResolver.ts +85 -0
- package/src/lib/bootstrap/dynamicLoader.ts +177 -0
- package/src/lib/bootstrap/factory.ts +108 -0
- package/src/lib/bootstrap/index.ts +23 -0
- package/src/lib/bootstrap/types.ts +31 -0
- package/src/lib/cache/segments.ts +56 -0
- package/src/lib/cli/progress.ts +55 -0
- package/src/lib/commands/__tests__/command-bus.test.ts +84 -0
- package/src/lib/commands/__tests__/helpers.test.ts +42 -0
- package/src/lib/commands/command-bus.ts +349 -0
- package/src/lib/commands/customFieldSnapshots.ts +86 -0
- package/src/lib/commands/helpers.ts +143 -0
- package/src/lib/commands/index.ts +4 -0
- package/src/lib/commands/operationMetadata.ts +40 -0
- package/src/lib/commands/registry.ts +46 -0
- package/src/lib/commands/scope.ts +59 -0
- package/src/lib/commands/types.ts +63 -0
- package/src/lib/crud/__tests__/crud-factory.test.ts +333 -0
- package/src/lib/crud/__tests__/custom-fields.test.ts +150 -0
- package/src/lib/crud/cache-stats.ts +127 -0
- package/src/lib/crud/cache.ts +205 -0
- package/src/lib/crud/custom-fields-client.ts +54 -0
- package/src/lib/crud/custom-fields.ts +607 -0
- package/src/lib/crud/errors.ts +23 -0
- package/src/lib/crud/exporters.ts +188 -0
- package/src/lib/crud/factory.ts +1622 -0
- package/src/lib/crud/types.ts +29 -0
- package/src/lib/custom-fields/normalize.ts +45 -0
- package/src/lib/data/engine.ts +562 -0
- package/src/lib/db/escapeLikePattern.ts +2 -0
- package/src/lib/db/mikro.ts +100 -0
- package/src/lib/di/container.ts +105 -0
- package/src/lib/email/send.ts +18 -0
- package/src/lib/encryption/__tests__/customFieldValues.test.ts +63 -0
- package/src/lib/encryption/__tests__/indexDoc.test.ts +115 -0
- package/src/lib/encryption/aes.ts +64 -0
- package/src/lib/encryption/customFieldValues.ts +67 -0
- package/src/lib/encryption/entityFields.ts +39 -0
- package/src/lib/encryption/entityIds.ts +107 -0
- package/src/lib/encryption/find.ts +81 -0
- package/src/lib/encryption/indexDoc.ts +104 -0
- package/src/lib/encryption/kms.ts +337 -0
- package/src/lib/encryption/subscriber.ts +416 -0
- package/src/lib/encryption/tenantDataEncryptionService.ts +313 -0
- package/src/lib/encryption/toggles.ts +15 -0
- package/src/lib/entities/naming.ts +6 -0
- package/src/lib/entities/system-entities.ts +43 -0
- package/src/lib/frontend/organizationEvents.ts +55 -0
- package/src/lib/frontend/useOrganizationScope.ts +30 -0
- package/src/lib/hotkeys/index.ts +168 -0
- package/src/lib/i18n/app-dictionaries.ts +18 -0
- package/src/lib/i18n/config.ts +4 -0
- package/src/lib/i18n/context.tsx +66 -0
- package/src/lib/i18n/server.ts +74 -0
- package/src/lib/i18n/translate.ts +54 -0
- package/src/lib/indexers/error-log.ts +106 -0
- package/src/lib/indexers/status-log.ts +119 -0
- package/src/lib/lib/auth/jwt.ts +39 -0
- package/src/lib/lib/auth/server.ts +94 -0
- package/src/lib/lib/email/send.ts +18 -0
- package/src/lib/lib/i18n/config.ts +4 -0
- package/src/lib/lib/i18n/context.tsx +38 -0
- package/src/lib/lib/utils.ts +6 -0
- package/src/lib/location/countries.ts +97 -0
- package/src/lib/modules/index.ts +1 -0
- package/src/lib/modules/registry.ts +18 -0
- package/src/lib/openapi/crud.ts +218 -0
- package/src/lib/openapi/generator.ts +1311 -0
- package/src/lib/openapi/index.ts +4 -0
- package/src/lib/openapi/sanitize.ts +137 -0
- package/src/lib/openapi/types.ts +79 -0
- package/src/lib/profiler/index.ts +371 -0
- package/src/lib/query/__tests__/engine.test.ts +274 -0
- package/src/lib/query/engine.ts +837 -0
- package/src/lib/query/join-utils.ts +238 -0
- package/src/lib/query/types.ts +121 -0
- package/src/lib/search/config.ts +49 -0
- package/src/lib/search/tokenize.ts +45 -0
- package/src/lib/slugify.ts +28 -0
- package/src/lib/testing/bootstrap.ts +124 -0
- package/src/lib/testing/index.ts +15 -0
- package/src/lib/testing/renderWithProviders.tsx +31 -0
- package/src/lib/url.ts +12 -0
- package/src/lib/utils.ts +17 -0
- package/src/lib/version.ts +5 -0
- package/src/modules/__tests__/dsl.test.ts +35 -0
- package/src/modules/__tests__/registry.test.ts +300 -0
- package/src/modules/dashboard/widgets.ts +57 -0
- package/src/modules/dsl.ts +32 -0
- package/src/modules/entities/__tests__/validation.test.ts +52 -0
- package/src/modules/entities/kinds.ts +20 -0
- package/src/modules/entities/options.ts +36 -0
- package/src/modules/entities/validation.ts +118 -0
- package/src/modules/entities/validators.ts +93 -0
- package/src/modules/entities.ts +102 -0
- package/src/modules/navigation/sidebarPreferences.ts +62 -0
- package/src/modules/perspectives/types.ts +40 -0
- package/src/modules/registry.ts +249 -0
- package/src/modules/search.ts +325 -0
- package/src/modules/vector.ts +122 -0
- package/src/modules/widgets/__tests__/injection.test.ts +48 -0
- package/src/modules/widgets/injection-loader.ts +235 -0
- package/src/modules/widgets/injection.ts +120 -0
- package/src/security/features.ts +22 -0
- package/src/types/pg.d.ts +2 -0
- package/src/types/react-email.d.ts +2 -0
- package/src/types/resend.d.ts +2 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +9 -0
- package/watch.mjs +6 -0
|
@@ -0,0 +1,1131 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
3
|
+
function resolveType(def) {
|
|
4
|
+
if (!def) return void 0;
|
|
5
|
+
if (typeof def.typeName === "string" && def.typeName.length) return def.typeName;
|
|
6
|
+
if (typeof def.type === "string" && def.type.length) return def.type;
|
|
7
|
+
return void 0;
|
|
8
|
+
}
|
|
9
|
+
function getShape(def) {
|
|
10
|
+
if (!def) return {};
|
|
11
|
+
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
12
|
+
if (shape && typeof shape === "object") return shape;
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
function normalizeChecks(checks) {
|
|
16
|
+
if (!Array.isArray(checks)) return [];
|
|
17
|
+
return checks.map((check) => {
|
|
18
|
+
if (!check) return {};
|
|
19
|
+
const base = check?._zod?.def ?? check?.def ?? check;
|
|
20
|
+
const kind = typeof check?.kind === "string" ? check.kind : typeof base?.check === "string" ? base.check : void 0;
|
|
21
|
+
const value = base?.value ?? base?.minimum ?? base?.maximum ?? base?.exact ?? base?.length ?? base?.limit ?? base?.includes ?? base?.min ?? base?.max;
|
|
22
|
+
return { kind, value, extra: base && typeof base === "object" ? base : void 0 };
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const DEFAULT_EXAMPLE_VALUES = {
|
|
26
|
+
string: "string",
|
|
27
|
+
number: 1,
|
|
28
|
+
integer: 1,
|
|
29
|
+
boolean: true,
|
|
30
|
+
uuid: "00000000-0000-4000-8000-000000000000",
|
|
31
|
+
email: "user@example.com",
|
|
32
|
+
url: "https://example.com/resource",
|
|
33
|
+
datetime: (/* @__PURE__ */ new Date("2025-01-01T00:00:00.000Z")).toISOString()
|
|
34
|
+
};
|
|
35
|
+
function toTitle(str) {
|
|
36
|
+
if (!str) return "";
|
|
37
|
+
return str.split(/[_\-\s]+/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
38
|
+
}
|
|
39
|
+
function normalizePath(path) {
|
|
40
|
+
const segments = path.split("/").filter((seg) => seg.length > 0);
|
|
41
|
+
const params = [];
|
|
42
|
+
const normalized = segments.map((seg) => {
|
|
43
|
+
const catchAll = seg.match(/^\[\.\.\.(.+)\]$/);
|
|
44
|
+
if (catchAll) {
|
|
45
|
+
params.push({ name: catchAll[1], catchAll: true });
|
|
46
|
+
return `{${catchAll[1]}}`;
|
|
47
|
+
}
|
|
48
|
+
const optCatchAll = seg.match(/^\[\[\.\.\.(.+)\]\]$/);
|
|
49
|
+
if (optCatchAll) {
|
|
50
|
+
params.push({ name: optCatchAll[1], catchAll: true, optional: true });
|
|
51
|
+
return `{${optCatchAll[1]}}`;
|
|
52
|
+
}
|
|
53
|
+
const dyn = seg.match(/^\[(.+)\]$/);
|
|
54
|
+
if (dyn) {
|
|
55
|
+
params.push({ name: dyn[1] });
|
|
56
|
+
return `{${dyn[1]}}`;
|
|
57
|
+
}
|
|
58
|
+
return seg;
|
|
59
|
+
}).join("/");
|
|
60
|
+
return { path: "/" + normalized, params };
|
|
61
|
+
}
|
|
62
|
+
function unwrap(schema) {
|
|
63
|
+
if (!schema) {
|
|
64
|
+
return { schema: void 0, optional: true, nullable: false };
|
|
65
|
+
}
|
|
66
|
+
let current = schema;
|
|
67
|
+
let optional = false;
|
|
68
|
+
let nullable = false;
|
|
69
|
+
let defaultValue;
|
|
70
|
+
while (true) {
|
|
71
|
+
const def = current?._def;
|
|
72
|
+
if (!def) {
|
|
73
|
+
return { schema: current, optional, nullable, defaultValue };
|
|
74
|
+
}
|
|
75
|
+
const typeName = resolveType(def);
|
|
76
|
+
if (typeName === "ZodOptional" || typeName === "optional") {
|
|
77
|
+
optional = true;
|
|
78
|
+
current = current._def.innerType;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (typeName === "ZodNullable" || typeName === "nullable") {
|
|
82
|
+
nullable = true;
|
|
83
|
+
current = current._def.innerType;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (typeName === "ZodDefault" || typeName === "default") {
|
|
87
|
+
optional = true;
|
|
88
|
+
const rawDefault = current._def.defaultValue;
|
|
89
|
+
defaultValue = typeof rawDefault === "function" ? rawDefault() : rawDefault;
|
|
90
|
+
current = current._def.innerType;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (typeName === "ZodPipeline" || typeName === "pipe") {
|
|
94
|
+
current = current._def.out ?? current._def.innerType ?? current._def.schema;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (typeName === "transformer") {
|
|
98
|
+
current = current._def.output;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (typeName === "ZodLazy" || typeName === "lazy") {
|
|
102
|
+
const getter = current._def.getter;
|
|
103
|
+
current = typeof getter === "function" ? getter() : current;
|
|
104
|
+
if (current === schema) break;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (typeName === "ZodPromise" || typeName === "promise") {
|
|
108
|
+
current = current._def.type;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (typeName === "ZodCatch" || typeName === "catch") {
|
|
112
|
+
current = current._def.innerType;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (typeName === "ZodReadonly" || typeName === "readonly") {
|
|
116
|
+
current = current._def.innerType;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (typeName === "ZodBranded" || typeName === "branded") {
|
|
120
|
+
current = current._def.type;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
return { schema: current, optional, nullable, defaultValue };
|
|
126
|
+
}
|
|
127
|
+
function zodToJsonSchema(schema, ctx) {
|
|
128
|
+
if (!schema) return void 0;
|
|
129
|
+
const context = ctx ?? { memo: /* @__PURE__ */ new WeakMap() };
|
|
130
|
+
const cached = context.memo.get(schema);
|
|
131
|
+
if (cached) return cached;
|
|
132
|
+
const placeholder = {};
|
|
133
|
+
context.memo.set(schema, placeholder);
|
|
134
|
+
const { schema: inner, nullable } = unwrap(schema);
|
|
135
|
+
if (!inner) {
|
|
136
|
+
if (nullable) placeholder.nullable = true;
|
|
137
|
+
return placeholder;
|
|
138
|
+
}
|
|
139
|
+
if (inner !== schema && typeof inner === "object") {
|
|
140
|
+
if (!context.memo.has(inner)) {
|
|
141
|
+
context.memo.set(inner, placeholder);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const def = inner._def;
|
|
145
|
+
if (!def) return placeholder;
|
|
146
|
+
const typeName = resolveType(def);
|
|
147
|
+
const result = placeholder;
|
|
148
|
+
switch (typeName) {
|
|
149
|
+
case "ZodString":
|
|
150
|
+
case "string": {
|
|
151
|
+
result.type = "string";
|
|
152
|
+
const checks = normalizeChecks(def.checks);
|
|
153
|
+
for (const check of checks) {
|
|
154
|
+
if (!check.kind) continue;
|
|
155
|
+
if (check.kind === "uuid" || check.extra?.format === "uuid") {
|
|
156
|
+
result.format = "uuid";
|
|
157
|
+
} else if (check.kind === "email" || check.extra?.format === "email") {
|
|
158
|
+
result.format = "email";
|
|
159
|
+
} else if (check.kind === "url" || check.extra?.format === "uri" || check.extra?.format === "url") {
|
|
160
|
+
result.format = "uri";
|
|
161
|
+
} else if (check.kind === "regex" && check.extra?.pattern instanceof RegExp) {
|
|
162
|
+
result.pattern = check.extra.pattern.source;
|
|
163
|
+
} else if (check.kind === "string_format" && typeof check.extra?.format === "string") {
|
|
164
|
+
result.format = check.extra.format;
|
|
165
|
+
} else if (check.kind === "datetime" || check.extra?.format === "date-time") {
|
|
166
|
+
result.format = "date-time";
|
|
167
|
+
} else if (["length", "len", "exact_length"].includes(check.kind ?? "")) {
|
|
168
|
+
if (typeof check.value === "number") {
|
|
169
|
+
result.minLength = check.value;
|
|
170
|
+
result.maxLength = check.value;
|
|
171
|
+
}
|
|
172
|
+
} else if (check.kind === "min" || check.kind === "min_length") {
|
|
173
|
+
if (typeof check.value === "number") result.minLength = check.value;
|
|
174
|
+
} else if (check.kind === "max" || check.kind === "max_length") {
|
|
175
|
+
if (typeof check.value === "number") result.maxLength = check.value;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
case "ZodNumber":
|
|
181
|
+
case "number": {
|
|
182
|
+
result.type = "number";
|
|
183
|
+
const checks = normalizeChecks(def.checks);
|
|
184
|
+
for (const check of checks) {
|
|
185
|
+
if (!check.kind) continue;
|
|
186
|
+
if (check.kind === "int" || check.kind === "isInteger") result.type = "integer";
|
|
187
|
+
if ((check.kind === "min" || check.kind === "gte") && typeof check.value === "number") result.minimum = check.value;
|
|
188
|
+
if ((check.kind === "max" || check.kind === "lte") && typeof check.value === "number") result.maximum = check.value;
|
|
189
|
+
if (check.kind === "multipleOf" && typeof check.value === "number") result.multipleOf = check.value;
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
case "ZodBigInt":
|
|
194
|
+
case "bigint":
|
|
195
|
+
result.type = "integer";
|
|
196
|
+
result.format = "int64";
|
|
197
|
+
break;
|
|
198
|
+
case "ZodBoolean":
|
|
199
|
+
case "boolean":
|
|
200
|
+
result.type = "boolean";
|
|
201
|
+
break;
|
|
202
|
+
case "ZodLiteral":
|
|
203
|
+
case "literal": {
|
|
204
|
+
const value = def.value ?? (Array.isArray(def.values) ? def.values[0] : void 0);
|
|
205
|
+
result.type = typeof value;
|
|
206
|
+
result.enum = [value];
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
case "ZodEnum":
|
|
210
|
+
case "enum": {
|
|
211
|
+
const entries = def.values ?? def.entries;
|
|
212
|
+
const values = Array.isArray(entries) ? entries : entries ? Object.values(entries) : [];
|
|
213
|
+
const enumerators = values.filter((v) => typeof v === "string" || typeof v === "number");
|
|
214
|
+
const allString = enumerators.every((v) => typeof v === "string");
|
|
215
|
+
result.type = allString ? "string" : "number";
|
|
216
|
+
result.enum = enumerators;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case "ZodNativeEnum": {
|
|
220
|
+
const values = Object.values(def.values).filter((v) => typeof v === "string" || typeof v === "number");
|
|
221
|
+
const allString = values.every((v) => typeof v === "string");
|
|
222
|
+
result.type = allString ? "string" : "number";
|
|
223
|
+
result.enum = values;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
case "ZodUnion":
|
|
227
|
+
case "union": {
|
|
228
|
+
const options = def.options || [];
|
|
229
|
+
result.oneOf = options.map((option) => zodToJsonSchema(option, context) ?? {});
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
case "ZodIntersection":
|
|
233
|
+
case "intersection": {
|
|
234
|
+
result.allOf = [
|
|
235
|
+
zodToJsonSchema(def.left, context) ?? {},
|
|
236
|
+
zodToJsonSchema(def.right, context) ?? {}
|
|
237
|
+
];
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
case "ZodPipeline":
|
|
241
|
+
case "pipe": {
|
|
242
|
+
const resolved = zodToJsonSchema(def.out ?? def.innerType ?? def.schema, context) ?? {};
|
|
243
|
+
Object.assign(result, resolved);
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
case "ZodLazy":
|
|
247
|
+
case "lazy": {
|
|
248
|
+
const next = typeof def.getter === "function" ? def.getter() : void 0;
|
|
249
|
+
const resolved = next ? zodToJsonSchema(next, context) : void 0;
|
|
250
|
+
if (resolved) Object.assign(result, resolved);
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
case "ZodPromise":
|
|
254
|
+
case "promise": {
|
|
255
|
+
const resolved = zodToJsonSchema(def.type, context);
|
|
256
|
+
if (resolved) Object.assign(result, resolved);
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
case "ZodCatch":
|
|
260
|
+
case "catch": {
|
|
261
|
+
const resolved = zodToJsonSchema(def.innerType ?? def.type, context);
|
|
262
|
+
if (resolved) Object.assign(result, resolved);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
case "ZodReadonly":
|
|
266
|
+
case "readonly": {
|
|
267
|
+
const resolved = zodToJsonSchema(def.innerType ?? def.type, context);
|
|
268
|
+
if (resolved) Object.assign(result, resolved);
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
case "ZodArray":
|
|
272
|
+
case "array": {
|
|
273
|
+
const elementSchema = def.type && typeof def.type === "object" ? def.type : def.element && typeof def.element === "object" ? def.element : void 0;
|
|
274
|
+
result.type = "array";
|
|
275
|
+
result.items = zodToJsonSchema(elementSchema, context) ?? {};
|
|
276
|
+
if (typeof def.minLength === "number") result.minItems = def.minLength;
|
|
277
|
+
if (typeof def.maxLength === "number") result.maxItems = def.maxLength;
|
|
278
|
+
const checks = normalizeChecks(def.checks);
|
|
279
|
+
for (const check of checks) {
|
|
280
|
+
if (check.kind === "min_length" && typeof check.value === "number") result.minItems = check.value;
|
|
281
|
+
if (check.kind === "max_length" && typeof check.value === "number") result.maxItems = check.value;
|
|
282
|
+
if (check.kind === "length" && typeof check.value === "number") {
|
|
283
|
+
result.minItems = check.value;
|
|
284
|
+
result.maxItems = check.value;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
case "ZodTuple":
|
|
290
|
+
case "tuple": {
|
|
291
|
+
const items = def.items || [];
|
|
292
|
+
result.type = "array";
|
|
293
|
+
result.prefixItems = items.map((item) => zodToJsonSchema(item, context) ?? {});
|
|
294
|
+
result.minItems = items.length;
|
|
295
|
+
result.maxItems = items.length;
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
case "ZodRecord":
|
|
299
|
+
case "record": {
|
|
300
|
+
result.type = "object";
|
|
301
|
+
result.additionalProperties = zodToJsonSchema(def.valueType ?? def.value, context) ?? {};
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
case "ZodObject":
|
|
305
|
+
case "object": {
|
|
306
|
+
result.type = "object";
|
|
307
|
+
const shape = getShape(def);
|
|
308
|
+
const properties = {};
|
|
309
|
+
const required = [];
|
|
310
|
+
for (const [key, rawSchema] of Object.entries(shape)) {
|
|
311
|
+
const unwrapped = unwrap(rawSchema);
|
|
312
|
+
const childSchema = zodToJsonSchema(unwrapped.schema, context);
|
|
313
|
+
if (!childSchema) continue;
|
|
314
|
+
const baseSchema = childSchema;
|
|
315
|
+
let propertySchema = baseSchema;
|
|
316
|
+
if (unwrapped.nullable) {
|
|
317
|
+
propertySchema = {
|
|
318
|
+
anyOf: [{ type: "null" }, propertySchema]
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
if (unwrapped.defaultValue !== void 0) {
|
|
322
|
+
if (propertySchema === baseSchema) {
|
|
323
|
+
propertySchema = { allOf: [baseSchema], default: unwrapped.defaultValue };
|
|
324
|
+
} else {
|
|
325
|
+
propertySchema = { ...propertySchema, default: unwrapped.defaultValue };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
properties[key] = propertySchema;
|
|
329
|
+
if (!unwrapped.optional) required.push(key);
|
|
330
|
+
}
|
|
331
|
+
result.properties = properties;
|
|
332
|
+
if (required.length > 0) result.required = required;
|
|
333
|
+
if (def.unknownKeys === "passthrough") {
|
|
334
|
+
result.additionalProperties = true;
|
|
335
|
+
} else if (def.catchall && resolveType(def.catchall._def) !== "ZodNever" && resolveType(def.catchall._def) !== "never") {
|
|
336
|
+
result.additionalProperties = zodToJsonSchema(def.catchall, context) ?? true;
|
|
337
|
+
} else {
|
|
338
|
+
result.additionalProperties = false;
|
|
339
|
+
}
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
case "ZodDate":
|
|
343
|
+
case "date":
|
|
344
|
+
result.type = "string";
|
|
345
|
+
result.format = "date-time";
|
|
346
|
+
break;
|
|
347
|
+
case "ZodNull":
|
|
348
|
+
case "null":
|
|
349
|
+
result.type = "null";
|
|
350
|
+
break;
|
|
351
|
+
case "ZodVoid":
|
|
352
|
+
case "void":
|
|
353
|
+
case "ZodNever":
|
|
354
|
+
case "never":
|
|
355
|
+
break;
|
|
356
|
+
case "ZodAny":
|
|
357
|
+
case "any":
|
|
358
|
+
case "ZodUnknown":
|
|
359
|
+
case "unknown":
|
|
360
|
+
case "ZodNaN":
|
|
361
|
+
case "nan":
|
|
362
|
+
default:
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
if (nullable) {
|
|
366
|
+
if (result.type && result.type !== "null") {
|
|
367
|
+
result.nullable = true;
|
|
368
|
+
} else if (!result.type) {
|
|
369
|
+
const clone = { ...result };
|
|
370
|
+
result.anyOf = [{ type: "null" }, clone];
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return result;
|
|
374
|
+
}
|
|
375
|
+
function generateExample(schema, ctx) {
|
|
376
|
+
if (!schema) return void 0;
|
|
377
|
+
if ((typeof schema !== "object" || schema === null) && typeof schema !== "function") return void 0;
|
|
378
|
+
const trackable = schema;
|
|
379
|
+
const context = ctx ?? { stack: /* @__PURE__ */ new WeakSet() };
|
|
380
|
+
if (context.stack.has(trackable)) return void 0;
|
|
381
|
+
context.stack.add(trackable);
|
|
382
|
+
try {
|
|
383
|
+
const { schema: inner, optional, nullable, defaultValue } = unwrap(schema);
|
|
384
|
+
if (!inner) {
|
|
385
|
+
if (defaultValue !== void 0) return defaultValue;
|
|
386
|
+
if (nullable) return null;
|
|
387
|
+
if (optional) return void 0;
|
|
388
|
+
return void 0;
|
|
389
|
+
}
|
|
390
|
+
const def = inner._def;
|
|
391
|
+
const typeName = resolveType(def);
|
|
392
|
+
if (defaultValue !== void 0) return defaultValue;
|
|
393
|
+
if (nullable) return null;
|
|
394
|
+
if (optional) return void 0;
|
|
395
|
+
switch (typeName) {
|
|
396
|
+
case "ZodString":
|
|
397
|
+
case "string": {
|
|
398
|
+
const checks = normalizeChecks(def?.checks);
|
|
399
|
+
for (const check of checks) {
|
|
400
|
+
if (!check.kind && !check.extra?.format) continue;
|
|
401
|
+
if (check.kind === "uuid" || check.extra?.format === "uuid") return DEFAULT_EXAMPLE_VALUES.uuid;
|
|
402
|
+
if (check.kind === "email" || check.extra?.format === "email") return DEFAULT_EXAMPLE_VALUES.email;
|
|
403
|
+
if (check.kind === "url" || check.extra?.format === "url" || check.extra?.format === "uri") return DEFAULT_EXAMPLE_VALUES.url;
|
|
404
|
+
if (check.kind === "datetime" || check.extra?.format === "date-time") return DEFAULT_EXAMPLE_VALUES.datetime;
|
|
405
|
+
}
|
|
406
|
+
return DEFAULT_EXAMPLE_VALUES.string;
|
|
407
|
+
}
|
|
408
|
+
case "ZodNumber":
|
|
409
|
+
case "number": {
|
|
410
|
+
const checks = normalizeChecks(def?.checks);
|
|
411
|
+
const isInt = checks.some((check) => check.kind === "int" || check.kind === "isInteger");
|
|
412
|
+
return isInt ? DEFAULT_EXAMPLE_VALUES.integer : DEFAULT_EXAMPLE_VALUES.number;
|
|
413
|
+
}
|
|
414
|
+
case "ZodBigInt":
|
|
415
|
+
case "bigint":
|
|
416
|
+
return BigInt(1);
|
|
417
|
+
case "ZodBoolean":
|
|
418
|
+
case "boolean":
|
|
419
|
+
return DEFAULT_EXAMPLE_VALUES.boolean;
|
|
420
|
+
case "ZodEnum":
|
|
421
|
+
case "enum": {
|
|
422
|
+
const entries = def?.values ?? def?.entries;
|
|
423
|
+
const values = Array.isArray(entries) ? entries : entries ? Object.values(entries) : [];
|
|
424
|
+
return values[0];
|
|
425
|
+
}
|
|
426
|
+
case "ZodNativeEnum": {
|
|
427
|
+
const values = Object.values(def?.values || []);
|
|
428
|
+
return values[0];
|
|
429
|
+
}
|
|
430
|
+
case "ZodLiteral":
|
|
431
|
+
case "literal":
|
|
432
|
+
return def?.value ?? (Array.isArray(def?.values) ? def.values[0] : void 0);
|
|
433
|
+
case "ZodArray":
|
|
434
|
+
case "array": {
|
|
435
|
+
const elementSchema = def?.type && typeof def.type === "object" ? def.type : def?.element && typeof def.element === "object" ? def.element : void 0;
|
|
436
|
+
const child = generateExample(elementSchema, context);
|
|
437
|
+
return child === void 0 ? [] : [child];
|
|
438
|
+
}
|
|
439
|
+
case "ZodTuple":
|
|
440
|
+
case "tuple": {
|
|
441
|
+
const items = def?.items || [];
|
|
442
|
+
return items.map((item) => generateExample(item, context));
|
|
443
|
+
}
|
|
444
|
+
case "ZodObject":
|
|
445
|
+
case "object": {
|
|
446
|
+
const shape = getShape(def);
|
|
447
|
+
const obj = {};
|
|
448
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
449
|
+
const example = generateExample(value, context);
|
|
450
|
+
if (example !== void 0) obj[key] = example;
|
|
451
|
+
}
|
|
452
|
+
return obj;
|
|
453
|
+
}
|
|
454
|
+
case "ZodRecord":
|
|
455
|
+
case "record": {
|
|
456
|
+
const valueExample = generateExample(def?.valueType ?? def?.value, context);
|
|
457
|
+
return valueExample === void 0 ? {} : { key: valueExample };
|
|
458
|
+
}
|
|
459
|
+
case "ZodUnion":
|
|
460
|
+
case "union": {
|
|
461
|
+
const options = def?.options || [];
|
|
462
|
+
return options.length ? generateExample(options[0], context) : void 0;
|
|
463
|
+
}
|
|
464
|
+
case "ZodPipeline":
|
|
465
|
+
case "pipe":
|
|
466
|
+
return generateExample(def?.out ?? def?.innerType ?? def?.schema, context);
|
|
467
|
+
case "ZodLazy":
|
|
468
|
+
case "lazy": {
|
|
469
|
+
const next = typeof def?.getter === "function" ? def.getter() : void 0;
|
|
470
|
+
return next ? generateExample(next, context) : void 0;
|
|
471
|
+
}
|
|
472
|
+
case "ZodPromise":
|
|
473
|
+
case "promise":
|
|
474
|
+
return generateExample(def?.type, context);
|
|
475
|
+
case "ZodCatch":
|
|
476
|
+
case "catch":
|
|
477
|
+
return generateExample(def?.innerType ?? def?.type, context);
|
|
478
|
+
case "ZodReadonly":
|
|
479
|
+
case "readonly":
|
|
480
|
+
return generateExample(def?.innerType ?? def?.type, context);
|
|
481
|
+
case "ZodIntersection":
|
|
482
|
+
case "intersection": {
|
|
483
|
+
const left = generateExample(def?.left, context);
|
|
484
|
+
const right = generateExample(def?.right, context);
|
|
485
|
+
if (typeof left === "object" && left && typeof right === "object" && right) {
|
|
486
|
+
return { ...left, ...right };
|
|
487
|
+
}
|
|
488
|
+
return left ?? right;
|
|
489
|
+
}
|
|
490
|
+
case "ZodDate":
|
|
491
|
+
case "date":
|
|
492
|
+
return DEFAULT_EXAMPLE_VALUES.datetime;
|
|
493
|
+
default:
|
|
494
|
+
return void 0;
|
|
495
|
+
}
|
|
496
|
+
} finally {
|
|
497
|
+
context.stack.delete(trackable);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
function buildParameters(schema, location, pathParamNames) {
|
|
501
|
+
if (!schema && location !== "path") return [];
|
|
502
|
+
const params = [];
|
|
503
|
+
if (location === "path" && pathParamNames && pathParamNames.length) {
|
|
504
|
+
const merged = mergePathParamSchemas(schema, pathParamNames);
|
|
505
|
+
for (const { name, schema: paramSchema, optional } of merged) {
|
|
506
|
+
const jsonSchema = zodToJsonSchema(paramSchema);
|
|
507
|
+
const example = generateExample(paramSchema);
|
|
508
|
+
params.push({
|
|
509
|
+
name,
|
|
510
|
+
in: "path",
|
|
511
|
+
required: !optional,
|
|
512
|
+
schema: jsonSchema ?? { type: "string" },
|
|
513
|
+
example
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
return params;
|
|
517
|
+
}
|
|
518
|
+
if (!schema) return params;
|
|
519
|
+
const { schema: unwrapped } = unwrap(schema);
|
|
520
|
+
if (!unwrapped) return params;
|
|
521
|
+
const def = unwrapped._def;
|
|
522
|
+
const typeName = resolveType(def);
|
|
523
|
+
if (typeName === "ZodObject" || typeName === "object") {
|
|
524
|
+
const shape = getShape(def);
|
|
525
|
+
for (const [key, raw] of Object.entries(shape)) {
|
|
526
|
+
const details = unwrap(raw);
|
|
527
|
+
if (!details.schema) continue;
|
|
528
|
+
const jsonSchema = zodToJsonSchema(details.schema);
|
|
529
|
+
const example = generateExample(details.schema);
|
|
530
|
+
params.push({
|
|
531
|
+
name: key,
|
|
532
|
+
in: location,
|
|
533
|
+
required: location === "path" ? true : !details.optional,
|
|
534
|
+
schema: jsonSchema ?? {},
|
|
535
|
+
example
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
const jsonSchema = zodToJsonSchema(unwrapped);
|
|
540
|
+
const example = generateExample(unwrapped);
|
|
541
|
+
params.push({
|
|
542
|
+
name: location === "header" ? "X-Custom-Header" : "value",
|
|
543
|
+
in: location,
|
|
544
|
+
required: location === "path",
|
|
545
|
+
schema: jsonSchema ?? {},
|
|
546
|
+
example
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
return params;
|
|
550
|
+
}
|
|
551
|
+
function mergePathParamSchemas(schema, params) {
|
|
552
|
+
const merged = [];
|
|
553
|
+
const map = {};
|
|
554
|
+
if (schema) {
|
|
555
|
+
const { schema: unwrapped } = unwrap(schema);
|
|
556
|
+
if (unwrapped && unwrapped._def && (resolveType(unwrapped._def) === "ZodObject" || resolveType(unwrapped._def) === "object")) {
|
|
557
|
+
const shape = getShape(unwrapped._def);
|
|
558
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
559
|
+
map[key] = value;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
for (const param of params) {
|
|
564
|
+
merged.push({
|
|
565
|
+
name: param.name,
|
|
566
|
+
schema: map[param.name],
|
|
567
|
+
optional: !!param.optional
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
return merged;
|
|
571
|
+
}
|
|
572
|
+
function buildRequestBody(request) {
|
|
573
|
+
if (!request) return void 0;
|
|
574
|
+
const schema = zodToJsonSchema(request.schema);
|
|
575
|
+
const example = request.example ?? generateExample(request.schema);
|
|
576
|
+
const contentType = request.contentType ?? "application/json";
|
|
577
|
+
return {
|
|
578
|
+
required: true,
|
|
579
|
+
content: {
|
|
580
|
+
[contentType]: {
|
|
581
|
+
schema: schema ?? {},
|
|
582
|
+
example
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
description: request.description
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function buildResponses(method, responses, errors, metadata) {
|
|
589
|
+
const entries = {};
|
|
590
|
+
const list = [...responses ?? []];
|
|
591
|
+
const errorList = [...errors ?? []];
|
|
592
|
+
if (metadata?.requireAuth) {
|
|
593
|
+
errorList.push({
|
|
594
|
+
status: 401,
|
|
595
|
+
description: "Unauthorized",
|
|
596
|
+
schema: z.object({ error: z.string() }),
|
|
597
|
+
xAutoGenerated: true
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
if (Array.isArray(metadata?.requireFeatures) && metadata.requireFeatures.length) {
|
|
601
|
+
errorList.push({
|
|
602
|
+
status: 403,
|
|
603
|
+
description: "Forbidden \u2013 missing required features",
|
|
604
|
+
schema: z.object({ error: z.string() }),
|
|
605
|
+
xAutoGenerated: true
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
if (!list.some((res) => res.status >= 200 && res.status < 300)) {
|
|
609
|
+
const fallbackStatus = method === "POST" ? 201 : method === "DELETE" ? 204 : 200;
|
|
610
|
+
list.push({
|
|
611
|
+
status: fallbackStatus,
|
|
612
|
+
description: fallbackStatus === 204 ? "Success" : "Success response"
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
for (const res of [...list, ...errorList]) {
|
|
616
|
+
const status = String(res.status || 200);
|
|
617
|
+
const mediaType = res.mediaType ?? "application/json";
|
|
618
|
+
const schema = res.schema ? zodToJsonSchema(res.schema) : void 0;
|
|
619
|
+
const example = res.schema ? res.example ?? generateExample(res.schema) : res.example;
|
|
620
|
+
const isNoContent = res.status === 204;
|
|
621
|
+
entries[status] = {
|
|
622
|
+
description: res.description ?? "",
|
|
623
|
+
...isNoContent ? {} : {
|
|
624
|
+
content: {
|
|
625
|
+
[mediaType]: {
|
|
626
|
+
schema: schema ?? { type: "object" },
|
|
627
|
+
...example !== void 0 ? { example } : {}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
...res.xAutoGenerated ? { "x-autoGenerated": true } : {}
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
return entries;
|
|
635
|
+
}
|
|
636
|
+
function buildSecurity(metadata, methodDoc, defaults) {
|
|
637
|
+
const securitySchemes = /* @__PURE__ */ new Set();
|
|
638
|
+
if (Array.isArray(methodDoc?.security)) methodDoc.security.forEach((s) => securitySchemes.add(s));
|
|
639
|
+
if (metadata?.requireAuth) securitySchemes.add("bearerAuth");
|
|
640
|
+
if (defaults) defaults.forEach((s) => securitySchemes.add(s));
|
|
641
|
+
if (securitySchemes.size === 0) return void 0;
|
|
642
|
+
return Array.from(securitySchemes.values()).map((scheme) => ({ [scheme]: [] }));
|
|
643
|
+
}
|
|
644
|
+
function collectExamples(querySchema, bodySchema, pathSchema, headerSchema, metadata) {
|
|
645
|
+
const examples = {};
|
|
646
|
+
const queryExample = querySchema ? generateExample(querySchema) : void 0;
|
|
647
|
+
if (queryExample && typeof queryExample === "object") examples.query = queryExample;
|
|
648
|
+
const bodyExample = bodySchema ? generateExample(bodySchema) : void 0;
|
|
649
|
+
if (bodyExample !== void 0) examples.body = bodyExample;
|
|
650
|
+
const pathExample = pathSchema ? generateExample(pathSchema) : void 0;
|
|
651
|
+
if (pathExample && typeof pathExample === "object") examples.path = pathExample;
|
|
652
|
+
const headerExample = headerSchema ? generateExample(headerSchema) : void 0;
|
|
653
|
+
if (headerExample && typeof headerExample === "object") examples.headers = headerExample;
|
|
654
|
+
if (metadata?.requireAuth) {
|
|
655
|
+
if (!examples.headers) examples.headers = {};
|
|
656
|
+
if (typeof examples.headers.authorization !== "string") {
|
|
657
|
+
examples.headers.authorization = "Bearer <token>";
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return examples;
|
|
661
|
+
}
|
|
662
|
+
function toFormUrlEncoded(value) {
|
|
663
|
+
if (!value || typeof value !== "object") return "";
|
|
664
|
+
const params = new URLSearchParams();
|
|
665
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
666
|
+
if (raw === void 0) continue;
|
|
667
|
+
params.append(key, raw === null ? "" : String(raw));
|
|
668
|
+
}
|
|
669
|
+
return params.toString();
|
|
670
|
+
}
|
|
671
|
+
function stringifyBodyExample(value, mediaType) {
|
|
672
|
+
if (value === void 0) return "";
|
|
673
|
+
if (mediaType === "application/x-www-form-urlencoded") {
|
|
674
|
+
return toFormUrlEncoded(value);
|
|
675
|
+
}
|
|
676
|
+
if (!mediaType || mediaType === "application/json") {
|
|
677
|
+
try {
|
|
678
|
+
return JSON.stringify(value, null, 2);
|
|
679
|
+
} catch {
|
|
680
|
+
return "";
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
if (typeof value === "string") return value;
|
|
684
|
+
try {
|
|
685
|
+
return JSON.stringify(value, null, 2);
|
|
686
|
+
} catch {
|
|
687
|
+
return String(value);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
function buildQueryString(example) {
|
|
691
|
+
if (!example || typeof example !== "object") return "";
|
|
692
|
+
const parts = [];
|
|
693
|
+
for (const [key, value] of Object.entries(example)) {
|
|
694
|
+
if (value === void 0 || value === null) continue;
|
|
695
|
+
const encoded = encodeURIComponent(String(value));
|
|
696
|
+
parts.push(`${encodeURIComponent(key)}=${encoded}`);
|
|
697
|
+
}
|
|
698
|
+
return parts.length ? `?${parts.join("&")}` : "";
|
|
699
|
+
}
|
|
700
|
+
function injectPathExamples(path, params, examples) {
|
|
701
|
+
if (!params.length) return path;
|
|
702
|
+
let result = path;
|
|
703
|
+
for (const param of params) {
|
|
704
|
+
const placeholder = `{${param.name}}`;
|
|
705
|
+
const example = examples && examples[param.name] !== void 0 ? examples[param.name] : `:${param.name}`;
|
|
706
|
+
result = result.replace(placeholder, String(example));
|
|
707
|
+
}
|
|
708
|
+
return result;
|
|
709
|
+
}
|
|
710
|
+
function buildCurlSample(method, path, params, examples, baseUrl, metadata, requestBody) {
|
|
711
|
+
const lines = [];
|
|
712
|
+
const pathWithExamples = injectPathExamples(path, params, examples.path);
|
|
713
|
+
const query = buildQueryString(examples.query);
|
|
714
|
+
const url = baseUrl.replace(/\/$/, "") + pathWithExamples + query;
|
|
715
|
+
lines.push(`curl -X ${method} "${url}"`);
|
|
716
|
+
lines.push(' -H "Accept: application/json"');
|
|
717
|
+
const headers = { ...examples.headers ?? {} };
|
|
718
|
+
if (metadata?.requireAuth && !headers.Authorization && !headers.authorization) {
|
|
719
|
+
headers.Authorization = "Bearer <token>";
|
|
720
|
+
}
|
|
721
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
722
|
+
lines.push(` -H "${key.replace(/"/g, "")}: ${String(value).replace(/"/g, "")}"`);
|
|
723
|
+
}
|
|
724
|
+
const bodyExample = examples.body ?? requestBody?.example;
|
|
725
|
+
const requestContentType = requestBody?.contentType ?? "application/json";
|
|
726
|
+
if (bodyExample !== void 0) {
|
|
727
|
+
lines.push(` -H "Content-Type: ${requestContentType}"`);
|
|
728
|
+
const serialized = stringifyBodyExample(bodyExample, requestContentType);
|
|
729
|
+
if (serialized) {
|
|
730
|
+
const escapedSerialized = escapeShellDoubleQuotes(serialized);
|
|
731
|
+
lines.push(` -d "${escapedSerialized}"`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return lines.join(" \\\n");
|
|
735
|
+
}
|
|
736
|
+
function escapeShellDoubleQuotes(value) {
|
|
737
|
+
return value.replace(/[\\`"$]/g, "\\$&");
|
|
738
|
+
}
|
|
739
|
+
function ensureSecurityComponents(doc) {
|
|
740
|
+
if (!doc.components) doc.components = {};
|
|
741
|
+
if (!doc.components.securitySchemes) doc.components.securitySchemes = {};
|
|
742
|
+
if (!doc.components.securitySchemes.bearerAuth) {
|
|
743
|
+
doc.components.securitySchemes.bearerAuth = {
|
|
744
|
+
type: "http",
|
|
745
|
+
scheme: "bearer",
|
|
746
|
+
bearerFormat: "JWT",
|
|
747
|
+
description: "Send an `Authorization: Bearer <token>` header with a valid API token."
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
function resolveOperationId(moduleId, path, method) {
|
|
752
|
+
const cleaned = normalizeOperationIdSegment(path);
|
|
753
|
+
return [moduleId, method.toLowerCase(), cleaned].filter(Boolean).join("_");
|
|
754
|
+
}
|
|
755
|
+
function normalizeOperationIdSegment(input) {
|
|
756
|
+
let output = "";
|
|
757
|
+
let previousUnderscore = false;
|
|
758
|
+
for (const character of input) {
|
|
759
|
+
const codePoint = character.charCodeAt(0);
|
|
760
|
+
const isLower = codePoint >= 97 && codePoint <= 122;
|
|
761
|
+
const isUpper = codePoint >= 65 && codePoint <= 90;
|
|
762
|
+
const isNumber = codePoint >= 48 && codePoint <= 57;
|
|
763
|
+
const isAlphaNumeric = isLower || isUpper || isNumber;
|
|
764
|
+
if (isAlphaNumeric) {
|
|
765
|
+
output += character;
|
|
766
|
+
previousUnderscore = false;
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
if (!previousUnderscore) {
|
|
770
|
+
output += "_";
|
|
771
|
+
previousUnderscore = true;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
while (output.startsWith("_")) output = output.slice(1);
|
|
775
|
+
while (output.endsWith("_")) output = output.slice(0, -1);
|
|
776
|
+
return output;
|
|
777
|
+
}
|
|
778
|
+
function collectRouteDoc(api, moduleId) {
|
|
779
|
+
if ("handlers" in api) {
|
|
780
|
+
const route = api;
|
|
781
|
+
if (route.docs) return route.docs;
|
|
782
|
+
const maybe = route.handlers?.openApi;
|
|
783
|
+
if (maybe && typeof maybe === "object") return maybe;
|
|
784
|
+
} else {
|
|
785
|
+
const legacy = api;
|
|
786
|
+
if (legacy.docs) {
|
|
787
|
+
return {
|
|
788
|
+
methods: { [legacy.method]: legacy.docs }
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
const maybe = legacy.handler?.openApi;
|
|
792
|
+
if (maybe && typeof maybe === "object") {
|
|
793
|
+
return {
|
|
794
|
+
methods: { [legacy.method]: maybe }
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return void 0;
|
|
799
|
+
}
|
|
800
|
+
function buildOpenApiDocument(modules, options = {}) {
|
|
801
|
+
const doc = {
|
|
802
|
+
openapi: "3.1.0",
|
|
803
|
+
info: {
|
|
804
|
+
title: options.title ?? "Open Mercato API",
|
|
805
|
+
version: options.version ?? "1.0.0",
|
|
806
|
+
description: options.description
|
|
807
|
+
},
|
|
808
|
+
servers: options.servers,
|
|
809
|
+
paths: {}
|
|
810
|
+
};
|
|
811
|
+
ensureSecurityComponents(doc);
|
|
812
|
+
const tags = /* @__PURE__ */ new Map();
|
|
813
|
+
for (const moduleEntry of modules) {
|
|
814
|
+
const defaultTag = moduleEntry.info?.title ?? toTitle(moduleEntry.id);
|
|
815
|
+
if (defaultTag) tags.set(defaultTag, moduleEntry.info?.description);
|
|
816
|
+
const apis = moduleEntry.apis ?? [];
|
|
817
|
+
for (const api of apis) {
|
|
818
|
+
const routeDoc = collectRouteDoc(api, moduleEntry.id);
|
|
819
|
+
const moduleTag = routeDoc?.tag ?? defaultTag;
|
|
820
|
+
const normalized = normalizePath(api.path ?? api.path ?? "");
|
|
821
|
+
const pathKey = normalized.path;
|
|
822
|
+
if (!doc.paths[pathKey]) doc.paths[pathKey] = {};
|
|
823
|
+
const availableMethods = "handlers" in api ? HTTP_METHODS.filter((method) => typeof api.handlers?.[method] === "function") : [api.method];
|
|
824
|
+
for (const method of availableMethods) {
|
|
825
|
+
const methodLower = method.toLowerCase();
|
|
826
|
+
const existing = doc.paths[pathKey][methodLower];
|
|
827
|
+
if (existing) continue;
|
|
828
|
+
const metadata = "handlers" in api ? api.metadata?.[method] : void 0;
|
|
829
|
+
const methodDoc = routeDoc?.methods?.[method];
|
|
830
|
+
const summary = methodDoc?.summary ?? routeDoc?.summary ?? `${method} ${pathKey}`;
|
|
831
|
+
const baseDescription = methodDoc?.description ?? routeDoc?.description;
|
|
832
|
+
const meta = metadata && typeof metadata === "object" ? metadata : void 0;
|
|
833
|
+
const requireFeatures = Array.isArray(meta?.["requireFeatures"]) ? meta["requireFeatures"] : void 0;
|
|
834
|
+
const requireRoles = Array.isArray(meta?.["requireRoles"]) ? meta["requireRoles"] : void 0;
|
|
835
|
+
const requireAuth = meta?.["requireAuth"] === true;
|
|
836
|
+
const descriptionParts = [];
|
|
837
|
+
if (baseDescription) descriptionParts.push(baseDescription);
|
|
838
|
+
if (Array.isArray(requireFeatures) && requireFeatures.length) {
|
|
839
|
+
descriptionParts.push(`Requires features: ${requireFeatures.join(", ")}`);
|
|
840
|
+
}
|
|
841
|
+
if (Array.isArray(requireRoles) && requireRoles.length) {
|
|
842
|
+
descriptionParts.push(`Requires roles: ${requireRoles.join(", ")}`);
|
|
843
|
+
}
|
|
844
|
+
const querySchema = methodDoc?.query;
|
|
845
|
+
const pathSchema = methodDoc?.pathParams ?? routeDoc?.pathParams;
|
|
846
|
+
const headerSchema = methodDoc?.headers;
|
|
847
|
+
const requestBody = methodDoc?.requestBody;
|
|
848
|
+
const examples = collectExamples(querySchema, requestBody?.schema, pathSchema, headerSchema, metadata);
|
|
849
|
+
const curlSample = buildCurlSample(
|
|
850
|
+
method,
|
|
851
|
+
pathKey,
|
|
852
|
+
normalized.params,
|
|
853
|
+
examples,
|
|
854
|
+
options.baseUrlForExamples ?? "https://api.open-mercato.local",
|
|
855
|
+
metadata,
|
|
856
|
+
requestBody
|
|
857
|
+
);
|
|
858
|
+
doc.paths[pathKey][methodLower] = {
|
|
859
|
+
operationId: methodDoc?.operationId ?? resolveOperationId(moduleEntry.id, pathKey, method),
|
|
860
|
+
summary,
|
|
861
|
+
description: descriptionParts.length ? descriptionParts.join("\n\n") : void 0,
|
|
862
|
+
tags: methodDoc?.tags ?? (moduleTag ? [moduleTag] : void 0),
|
|
863
|
+
deprecated: methodDoc?.deprecated,
|
|
864
|
+
externalDocs: methodDoc?.externalDocs,
|
|
865
|
+
parameters: [
|
|
866
|
+
...buildParameters(pathSchema, "path", normalized.params),
|
|
867
|
+
...buildParameters(querySchema, "query"),
|
|
868
|
+
...buildParameters(headerSchema, "header")
|
|
869
|
+
].filter(Boolean),
|
|
870
|
+
requestBody: buildRequestBody(requestBody),
|
|
871
|
+
responses: buildResponses(method, methodDoc?.responses, methodDoc?.errors, metadata),
|
|
872
|
+
security: buildSecurity(metadata, methodDoc, options.defaultSecurity),
|
|
873
|
+
"x-codeSamples": methodDoc?.codeSamples ?? [
|
|
874
|
+
{
|
|
875
|
+
lang: "curl",
|
|
876
|
+
label: "cURL",
|
|
877
|
+
source: curlSample
|
|
878
|
+
}
|
|
879
|
+
],
|
|
880
|
+
...Array.isArray(requireFeatures) && requireFeatures.length ? { "x-require-features": requireFeatures } : {},
|
|
881
|
+
...Array.isArray(requireRoles) && requireRoles.length ? { "x-require-roles": requireRoles } : {},
|
|
882
|
+
...requireAuth ? { "x-require-auth": true } : {},
|
|
883
|
+
...methodDoc?.extensions ?? {}
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
doc.tags = Array.from(tags.entries()).map(([name, description]) => ({
|
|
889
|
+
name,
|
|
890
|
+
description: description ?? void 0
|
|
891
|
+
}));
|
|
892
|
+
return doc;
|
|
893
|
+
}
|
|
894
|
+
function formatMarkdownTable(rows) {
|
|
895
|
+
if (!rows.length) return "";
|
|
896
|
+
const header = ["Name", "Location", "Type", "Description"];
|
|
897
|
+
const align = ["---", "---", "---", "---"];
|
|
898
|
+
const lines = [
|
|
899
|
+
`| ${header.join(" | ")} |`,
|
|
900
|
+
`| ${align.join(" | ")} |`,
|
|
901
|
+
...rows.map((row) => `| ${row.join(" | ")} |`)
|
|
902
|
+
];
|
|
903
|
+
return lines.join("\n");
|
|
904
|
+
}
|
|
905
|
+
function schemaTypeLabel(schema) {
|
|
906
|
+
if (!schema) return "any";
|
|
907
|
+
if (schema.type) return schema.type;
|
|
908
|
+
if (schema.oneOf) return schema.oneOf.map(schemaTypeLabel).join(" | ");
|
|
909
|
+
if (schema.allOf) return schema.allOf.map(schemaTypeLabel).join(" & ");
|
|
910
|
+
return "any";
|
|
911
|
+
}
|
|
912
|
+
function schemaHasDetails(schema) {
|
|
913
|
+
if (!schema || typeof schema !== "object") return false;
|
|
914
|
+
const schemaObj = schema;
|
|
915
|
+
if (Array.isArray(schemaObj.enum) && schemaObj.enum.length) return true;
|
|
916
|
+
if (schemaObj.const !== void 0) return true;
|
|
917
|
+
if (typeof schemaObj.format === "string") return true;
|
|
918
|
+
if (Array.isArray(schemaObj.oneOf) && schemaObj.oneOf.some((s) => schemaHasDetails(s))) return true;
|
|
919
|
+
if (Array.isArray(schemaObj.anyOf) && schemaObj.anyOf.some((s) => schemaHasDetails(s))) return true;
|
|
920
|
+
if (Array.isArray(schemaObj.allOf) && schemaObj.allOf.some((s) => schemaHasDetails(s))) return true;
|
|
921
|
+
if (schemaObj.items && schemaHasDetails(schemaObj.items)) return true;
|
|
922
|
+
if (schemaObj.properties && Object.keys(schemaObj.properties).length) return true;
|
|
923
|
+
if (Array.isArray(schemaObj.prefixItems) && schemaObj.prefixItems.some((s) => schemaHasDetails(s))) return true;
|
|
924
|
+
if (schemaObj.type && schemaObj.type !== "object") return true;
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
927
|
+
function selectContentVariant(content) {
|
|
928
|
+
if (!content) return void 0;
|
|
929
|
+
const entries = Object.entries(content);
|
|
930
|
+
if (!entries.length) return void 0;
|
|
931
|
+
const preferred = [
|
|
932
|
+
"application/json",
|
|
933
|
+
"application/x-www-form-urlencoded",
|
|
934
|
+
"multipart/form-data",
|
|
935
|
+
"text/plain"
|
|
936
|
+
];
|
|
937
|
+
for (const mediaType2 of preferred) {
|
|
938
|
+
const match = entries.find(([type]) => type === mediaType2);
|
|
939
|
+
if (match) {
|
|
940
|
+
const [selectedType, entry2] = match;
|
|
941
|
+
return { mediaType: selectedType, entry: entry2 };
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
const [mediaType, entry] = entries[0];
|
|
945
|
+
return { mediaType, entry };
|
|
946
|
+
}
|
|
947
|
+
function formatExampleForDisplay(example, mediaType) {
|
|
948
|
+
if (example === void 0) return null;
|
|
949
|
+
if (mediaType === "application/x-www-form-urlencoded") {
|
|
950
|
+
const encoded = toFormUrlEncoded(example);
|
|
951
|
+
if (!encoded) return null;
|
|
952
|
+
return { value: encoded, language: "text" };
|
|
953
|
+
}
|
|
954
|
+
if (mediaType === "multipart/form-data") {
|
|
955
|
+
if (example && typeof example === "object") {
|
|
956
|
+
const lines = Object.entries(example).map(([key, value]) => {
|
|
957
|
+
const rendered = value === void 0 || value === null ? "" : String(value);
|
|
958
|
+
return `${key}=${rendered}`;
|
|
959
|
+
});
|
|
960
|
+
if (lines.length) {
|
|
961
|
+
return { value: lines.join("\n"), language: "text" };
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
if (typeof example === "string") {
|
|
965
|
+
return { value: example, language: "text" };
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
if (!mediaType || mediaType === "application/json") {
|
|
969
|
+
try {
|
|
970
|
+
return { value: JSON.stringify(example, null, 2), language: "json" };
|
|
971
|
+
} catch {
|
|
972
|
+
return null;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
if (typeof example === "string") {
|
|
976
|
+
return { value: example, language: "text" };
|
|
977
|
+
}
|
|
978
|
+
try {
|
|
979
|
+
return { value: JSON.stringify(example, null, 2), language: "json" };
|
|
980
|
+
} catch {
|
|
981
|
+
return { value: String(example), language: "text" };
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
function formatSchemaForDisplay(schema) {
|
|
985
|
+
if (!schema) return null;
|
|
986
|
+
try {
|
|
987
|
+
return { value: JSON.stringify(schema, null, 2), language: "json" };
|
|
988
|
+
} catch {
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
function generateMarkdownFromOpenApi(doc) {
|
|
993
|
+
const lines = [];
|
|
994
|
+
lines.push(`# ${doc.info.title}`);
|
|
995
|
+
lines.push("");
|
|
996
|
+
lines.push(`Version: ${doc.info.version}`);
|
|
997
|
+
if (doc.info.description) {
|
|
998
|
+
lines.push("");
|
|
999
|
+
lines.push(doc.info.description);
|
|
1000
|
+
}
|
|
1001
|
+
if (doc.servers && doc.servers.length) {
|
|
1002
|
+
lines.push("");
|
|
1003
|
+
lines.push("## Servers");
|
|
1004
|
+
for (const server of doc.servers) {
|
|
1005
|
+
lines.push(`- ${server.url}${server.description ? ` \u2013 ${server.description}` : ""}`);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
const sortedPaths = Object.keys(doc.paths).sort();
|
|
1009
|
+
for (const path of sortedPaths) {
|
|
1010
|
+
const operations = doc.paths[path];
|
|
1011
|
+
const methods = Object.keys(operations).sort();
|
|
1012
|
+
for (const method of methods) {
|
|
1013
|
+
const op = operations[method];
|
|
1014
|
+
lines.push("");
|
|
1015
|
+
lines.push(`## ${method.toUpperCase()} \`${path}\``);
|
|
1016
|
+
if (op.summary) {
|
|
1017
|
+
lines.push("");
|
|
1018
|
+
lines.push(op.summary);
|
|
1019
|
+
}
|
|
1020
|
+
if (op.description) {
|
|
1021
|
+
lines.push("");
|
|
1022
|
+
lines.push(op.description);
|
|
1023
|
+
}
|
|
1024
|
+
if (op.tags && op.tags.length) {
|
|
1025
|
+
lines.push("");
|
|
1026
|
+
lines.push(`**Tags:** ${op.tags.join(", ")}`);
|
|
1027
|
+
}
|
|
1028
|
+
if (op["x-require-auth"]) {
|
|
1029
|
+
lines.push("");
|
|
1030
|
+
lines.push(`**Requires authentication.**`);
|
|
1031
|
+
}
|
|
1032
|
+
if (op["x-require-features"]) {
|
|
1033
|
+
lines.push("");
|
|
1034
|
+
lines.push(`**Features:** ${op["x-require-features"].join(", ")}`);
|
|
1035
|
+
}
|
|
1036
|
+
if (op["x-require-roles"]) {
|
|
1037
|
+
lines.push("");
|
|
1038
|
+
lines.push(`**Roles:** ${op["x-require-roles"].join(", ")}`);
|
|
1039
|
+
}
|
|
1040
|
+
const parameters = op.parameters ?? [];
|
|
1041
|
+
if (parameters.length) {
|
|
1042
|
+
lines.push("");
|
|
1043
|
+
lines.push("### Parameters");
|
|
1044
|
+
const rows = parameters.map((p) => [
|
|
1045
|
+
p.name,
|
|
1046
|
+
p.in,
|
|
1047
|
+
schemaTypeLabel(p.schema),
|
|
1048
|
+
p.required ? "Required" : "Optional"
|
|
1049
|
+
]);
|
|
1050
|
+
lines.push(formatMarkdownTable(rows));
|
|
1051
|
+
}
|
|
1052
|
+
if (op.requestBody) {
|
|
1053
|
+
const selection = selectContentVariant(op.requestBody.content);
|
|
1054
|
+
if (selection) {
|
|
1055
|
+
const { mediaType, entry } = selection;
|
|
1056
|
+
const example = entry?.example ?? entry?.examples?.default?.value;
|
|
1057
|
+
const formatted = formatExampleForDisplay(example, mediaType);
|
|
1058
|
+
const schemaFormatted = entry?.schema && schemaHasDetails(entry.schema) ? formatSchemaForDisplay(entry.schema) : null;
|
|
1059
|
+
lines.push("");
|
|
1060
|
+
lines.push("### Request Body");
|
|
1061
|
+
lines.push("");
|
|
1062
|
+
lines.push(`Content-Type: \`${mediaType}\``);
|
|
1063
|
+
if (formatted) {
|
|
1064
|
+
lines.push("");
|
|
1065
|
+
lines.push(`\`\`\`${formatted.language}`);
|
|
1066
|
+
lines.push(formatted.value);
|
|
1067
|
+
lines.push("```");
|
|
1068
|
+
} else if (schemaFormatted) {
|
|
1069
|
+
lines.push("");
|
|
1070
|
+
lines.push(`\`\`\`${schemaFormatted.language}`);
|
|
1071
|
+
lines.push(schemaFormatted.value);
|
|
1072
|
+
lines.push("```");
|
|
1073
|
+
} else {
|
|
1074
|
+
lines.push("");
|
|
1075
|
+
lines.push("No example available for this content type.");
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
const responses = op.responses ?? {};
|
|
1080
|
+
const responseStatuses = Object.keys(responses).sort();
|
|
1081
|
+
if (responseStatuses.length) {
|
|
1082
|
+
lines.push("");
|
|
1083
|
+
lines.push("### Responses");
|
|
1084
|
+
for (const status of responseStatuses) {
|
|
1085
|
+
const response = responses[status];
|
|
1086
|
+
if (response?.["x-autoGenerated"]) continue;
|
|
1087
|
+
lines.push("");
|
|
1088
|
+
lines.push(`**${status}** \u2013 ${response.description || "Response"}`);
|
|
1089
|
+
const selection = selectContentVariant(response.content);
|
|
1090
|
+
if (selection) {
|
|
1091
|
+
const { mediaType, entry } = selection;
|
|
1092
|
+
const example = entry?.example ?? entry?.examples?.default?.value;
|
|
1093
|
+
const formatted = formatExampleForDisplay(example, mediaType);
|
|
1094
|
+
const schemaFormatted = entry?.schema && schemaHasDetails(entry.schema) ? formatSchemaForDisplay(entry.schema) : null;
|
|
1095
|
+
lines.push("");
|
|
1096
|
+
lines.push(`Content-Type: \`${mediaType}\``);
|
|
1097
|
+
if (formatted) {
|
|
1098
|
+
lines.push("");
|
|
1099
|
+
lines.push(`\`\`\`${formatted.language}`);
|
|
1100
|
+
lines.push(formatted.value);
|
|
1101
|
+
lines.push("```");
|
|
1102
|
+
} else if (schemaFormatted) {
|
|
1103
|
+
lines.push("");
|
|
1104
|
+
lines.push(`\`\`\`${schemaFormatted.language}`);
|
|
1105
|
+
lines.push(schemaFormatted.value);
|
|
1106
|
+
lines.push("```");
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
const samples = op["x-codeSamples"];
|
|
1112
|
+
if (samples && samples.length) {
|
|
1113
|
+
const curl = samples.find((sample) => String(sample.lang).toLowerCase() === "curl") ?? samples[0];
|
|
1114
|
+
if (curl?.source) {
|
|
1115
|
+
lines.push("");
|
|
1116
|
+
lines.push("### Example");
|
|
1117
|
+
lines.push("");
|
|
1118
|
+
lines.push("```bash");
|
|
1119
|
+
lines.push(curl.source);
|
|
1120
|
+
lines.push("```");
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
return lines.join("\n");
|
|
1126
|
+
}
|
|
1127
|
+
export {
|
|
1128
|
+
buildOpenApiDocument,
|
|
1129
|
+
generateMarkdownFromOpenApi
|
|
1130
|
+
};
|
|
1131
|
+
//# sourceMappingURL=generator.js.map
|