zudoku 0.74.3 → 0.75.1
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/dist/cli/cli.js +121 -23
- package/dist/declarations/lib/authentication/providers/openid.d.ts +1 -0
- package/dist/declarations/lib/authentication/state.d.ts +6 -1
- package/dist/declarations/lib/plugins/api-catalog/Catalog.d.ts +2 -4
- package/dist/declarations/lib/plugins/api-catalog/index.d.ts +3 -8
- package/dist/declarations/lib/plugins/openapi/MCPEndpoint.d.ts +2 -1
- package/dist/declarations/lib/plugins/openapi/mcp-configs.d.ts +28 -0
- package/dist/declarations/lib/plugins/openapi/schema/SchemaRefLink.d.ts +5 -0
- package/dist/declarations/lib/plugins/openapi/schema/SchemaView.d.ts +2 -1
- package/dist/declarations/lib/plugins/openapi/schema/utils.d.ts +1 -0
- package/dist/declarations/lib/ui/HoverCard.d.ts +5 -5
- package/dist/declarations/lib/ui/Toggle.d.ts +2 -6
- package/dist/declarations/lib/ui/ToggleGroup.d.ts +7 -9
- package/docs/configuration/authentication-auth0.md +6 -3
- package/docs/configuration/authentication-openid.md +110 -0
- package/docs/configuration/authentication.md +5 -2
- package/docs/deploy/github-pages.md +12 -0
- package/docs/guides/redirects.md +186 -0
- package/docs/openapi-extensions/x-code-samples.md +57 -0
- package/docs/openapi-extensions/x-display-name.md +31 -0
- package/docs/openapi-extensions/x-mcp-server.md +104 -0
- package/docs/openapi-extensions/x-mcp.md +171 -0
- package/docs/openapi-extensions/x-tag-groups.md +65 -0
- package/docs/openapi-extensions/x-zudoku-collapsed.md +35 -0
- package/docs/openapi-extensions/x-zudoku-collapsible.md +35 -0
- package/docs/openapi-extensions/x-zudoku-playground-enabled.md +43 -0
- package/package.json +9 -8
- package/src/lib/authentication/providers/openid.tsx +18 -15
- package/src/lib/authentication/state.ts +14 -1
- package/src/lib/components/Markdown.tsx +34 -7
- package/src/lib/components/Meta.tsx +10 -1
- package/src/lib/hooks/useHotkey.ts +10 -0
- package/src/lib/oas/graphql/circular.ts +11 -0
- package/src/lib/plugins/api-catalog/Catalog.tsx +255 -52
- package/src/lib/plugins/api-catalog/index.tsx +7 -64
- package/src/lib/plugins/openapi/Endpoint.tsx +17 -26
- package/src/lib/plugins/openapi/MCPEndpoint.tsx +292 -209
- package/src/lib/plugins/openapi/ParamInfos.tsx +20 -7
- package/src/lib/plugins/openapi/SchemaList.tsx +1 -1
- package/src/lib/plugins/openapi/SecurityRequirements.tsx +86 -68
- package/src/lib/plugins/openapi/mcp-configs.ts +265 -0
- package/src/lib/plugins/openapi/playground/Playground.tsx +7 -7
- package/src/lib/plugins/openapi/schema/SchemaRefLink.tsx +21 -0
- package/src/lib/plugins/openapi/schema/SchemaView.tsx +22 -4
- package/src/lib/plugins/openapi/schema/utils.ts +21 -0
- package/src/lib/ui/Badge.tsx +1 -1
- package/src/lib/ui/HoverCard.tsx +35 -21
- package/src/lib/ui/Toggle.tsx +21 -21
- package/src/lib/ui/ToggleGroup.tsx +59 -32
- package/src/vite/build.ts +3 -1
- package/src/vite/plugin-api.ts +32 -0
- package/src/vite/prerender/prerender.ts +136 -16
- package/src/zuplo/enrich-with-zuplo-mcp.ts +104 -7
package/dist/cli/cli.js
CHANGED
|
@@ -3813,7 +3813,7 @@ import {
|
|
|
3813
3813
|
// package.json
|
|
3814
3814
|
var package_default = {
|
|
3815
3815
|
name: "zudoku",
|
|
3816
|
-
version: "0.
|
|
3816
|
+
version: "0.75.0",
|
|
3817
3817
|
type: "module",
|
|
3818
3818
|
sideEffects: [
|
|
3819
3819
|
"**/*.css",
|
|
@@ -3891,6 +3891,7 @@ var package_default = {
|
|
|
3891
3891
|
},
|
|
3892
3892
|
dependencies: {
|
|
3893
3893
|
"@apidevtools/json-schema-ref-parser": "15.3.1",
|
|
3894
|
+
"@base-ui/react": "^1.4.0",
|
|
3894
3895
|
"@envelop/core": "5.5.1",
|
|
3895
3896
|
"@graphql-typed-document-node/core": "3.2.0",
|
|
3896
3897
|
"@hono/node-server": "1.19.13",
|
|
@@ -3951,14 +3952,14 @@ var package_default = {
|
|
|
3951
3952
|
"fast-equals": "6.0.0",
|
|
3952
3953
|
glob: "13.0.6",
|
|
3953
3954
|
"glob-parent": "6.0.2",
|
|
3954
|
-
graphql: "16.13.
|
|
3955
|
+
graphql: "16.13.2",
|
|
3955
3956
|
"graphql-type-json": "0.3.2",
|
|
3956
3957
|
"graphql-yoga": "5.18.0",
|
|
3957
3958
|
"gray-matter": "4.0.3",
|
|
3958
3959
|
"hast-util-heading-rank": "3.0.0",
|
|
3959
3960
|
"hast-util-to-jsx-runtime": "2.3.6",
|
|
3960
3961
|
"hast-util-to-string": "3.0.1",
|
|
3961
|
-
hono: "4.12.
|
|
3962
|
+
hono: "4.12.14",
|
|
3962
3963
|
"http-terminator": "3.2.0",
|
|
3963
3964
|
"javascript-stringify": "2.1.0",
|
|
3964
3965
|
"json-schema-to-typescript-lite": "15.0.0",
|
|
@@ -3973,7 +3974,7 @@ var package_default = {
|
|
|
3973
3974
|
"next-themes": "0.4.6",
|
|
3974
3975
|
oauth4webapi: "3.8.5",
|
|
3975
3976
|
"openapi-types": "12.1.3",
|
|
3976
|
-
pagefind: "1.5.
|
|
3977
|
+
pagefind: "1.5.2",
|
|
3977
3978
|
picocolors: "1.1.1",
|
|
3978
3979
|
piscina: "5.1.4",
|
|
3979
3980
|
"posthog-node": "5.26.0",
|
|
@@ -4010,9 +4011,9 @@ var package_default = {
|
|
|
4010
4011
|
zustand: "5.0.12"
|
|
4011
4012
|
},
|
|
4012
4013
|
devDependencies: {
|
|
4013
|
-
"@clerk/clerk-js": "^6.
|
|
4014
|
-
"@graphql-codegen/cli": "6.
|
|
4015
|
-
"@inkeep/cxkit-types": "0.5.
|
|
4014
|
+
"@clerk/clerk-js": "^6.7.4",
|
|
4015
|
+
"@graphql-codegen/cli": "6.3.0",
|
|
4016
|
+
"@inkeep/cxkit-types": "0.5.117",
|
|
4016
4017
|
"@testing-library/dom": "catalog:",
|
|
4017
4018
|
"@testing-library/jest-dom": "catalog:",
|
|
4018
4019
|
"@testing-library/react": "catalog:",
|
|
@@ -4030,7 +4031,7 @@ var package_default = {
|
|
|
4030
4031
|
"@types/yargs": "17.0.35",
|
|
4031
4032
|
"@vitest/coverage-v8": "4.0.18",
|
|
4032
4033
|
"happy-dom": "catalog:",
|
|
4033
|
-
"oxc-parser": "^0.
|
|
4034
|
+
"oxc-parser": "^0.126.0",
|
|
4034
4035
|
react: "catalog:",
|
|
4035
4036
|
"react-dom": "catalog:",
|
|
4036
4037
|
tsx: "4.21.0",
|
|
@@ -4730,6 +4731,9 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
|
|
|
4730
4731
|
const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
|
|
4731
4732
|
Object.entries(obj).map(([k, v]) => [k, recurse(v, k)])
|
|
4732
4733
|
);
|
|
4734
|
+
if (!Array.isArray(result) && typeof refPath === "string" && !("__$ref" in result)) {
|
|
4735
|
+
result.__$ref = refPath;
|
|
4736
|
+
}
|
|
4733
4737
|
refs.set(obj, result);
|
|
4734
4738
|
currentPath.delete(obj);
|
|
4735
4739
|
if (typeof refPath === "string") currentRefPaths.delete(refPath);
|
|
@@ -6224,15 +6228,37 @@ var viteApiPlugin = async () => {
|
|
|
6224
6228
|
);
|
|
6225
6229
|
const apis = ensureArray(config2.apis);
|
|
6226
6230
|
const apiMetadata = [];
|
|
6231
|
+
const httpMethods = /* @__PURE__ */ new Set([
|
|
6232
|
+
"get",
|
|
6233
|
+
"post",
|
|
6234
|
+
"put",
|
|
6235
|
+
"patch",
|
|
6236
|
+
"delete",
|
|
6237
|
+
"options",
|
|
6238
|
+
"head",
|
|
6239
|
+
"trace"
|
|
6240
|
+
]);
|
|
6227
6241
|
for (const apiConfig of apis) {
|
|
6228
6242
|
if (apiConfig.type === "file" && apiConfig.path) {
|
|
6229
6243
|
const latestSchema = schemaManager.getLatestSchema(apiConfig.path);
|
|
6230
6244
|
if (!latestSchema?.schema.info) continue;
|
|
6245
|
+
const operationCount = Object.values(
|
|
6246
|
+
latestSchema.schema.paths ?? {}
|
|
6247
|
+
).reduce((sum, pathItem) => {
|
|
6248
|
+
if (!pathItem || typeof pathItem !== "object") return sum;
|
|
6249
|
+
return sum + Object.keys(pathItem).filter(
|
|
6250
|
+
(m) => httpMethods.has(m.toLowerCase())
|
|
6251
|
+
).length;
|
|
6252
|
+
}, 0);
|
|
6253
|
+
const rawVersion = latestSchema.schema.info.version;
|
|
6254
|
+
const version = rawVersion ? rawVersion.startsWith("v") || rawVersion.startsWith("V") ? rawVersion : `v${rawVersion}` : void 0;
|
|
6231
6255
|
apiMetadata.push({
|
|
6232
6256
|
path: apiConfig.path,
|
|
6233
6257
|
label: latestSchema.schema.info.title,
|
|
6234
6258
|
description: latestSchema.schema.info.description ?? "",
|
|
6235
|
-
categories: apiConfig.categories ?? []
|
|
6259
|
+
categories: apiConfig.categories ?? [],
|
|
6260
|
+
version,
|
|
6261
|
+
operationCount
|
|
6236
6262
|
});
|
|
6237
6263
|
}
|
|
6238
6264
|
}
|
|
@@ -7775,6 +7801,7 @@ async function writeOutput(dir, {
|
|
|
7775
7801
|
// src/vite/prerender/prerender.ts
|
|
7776
7802
|
init_logger();
|
|
7777
7803
|
init_file_exists();
|
|
7804
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
7778
7805
|
import { readFile as readFile2, rm } from "node:fs/promises";
|
|
7779
7806
|
import os from "node:os";
|
|
7780
7807
|
import path21 from "node:path";
|
|
@@ -7945,7 +7972,9 @@ var prerender = async ({
|
|
|
7945
7972
|
paths.push(joinUrl(r.from));
|
|
7946
7973
|
}
|
|
7947
7974
|
}
|
|
7948
|
-
const maxThreads
|
|
7975
|
+
const { maxThreads, maxOldGenerationSizeMb } = getWorkerScaling(
|
|
7976
|
+
buildConfig?.prerender?.workers
|
|
7977
|
+
);
|
|
7949
7978
|
const start = performance.now();
|
|
7950
7979
|
const LOG_INTERVAL_MS = 3e4;
|
|
7951
7980
|
let lastLogTime = start;
|
|
@@ -7974,7 +8003,11 @@ var prerender = async ({
|
|
|
7974
8003
|
const pool = new Piscina({
|
|
7975
8004
|
filename: new URL("./worker.js", import.meta.url).href,
|
|
7976
8005
|
idleTimeout: 5e3,
|
|
8006
|
+
minThreads: 1,
|
|
7977
8007
|
maxThreads,
|
|
8008
|
+
resourceLimits: {
|
|
8009
|
+
maxOldGenerationSizeMb
|
|
8010
|
+
},
|
|
7978
8011
|
workerData: {
|
|
7979
8012
|
template: html,
|
|
7980
8013
|
distDir,
|
|
@@ -7986,12 +8019,6 @@ var prerender = async ({
|
|
|
7986
8019
|
const workerResults = await Promise.all(
|
|
7987
8020
|
paths.map(async (urlPath) => {
|
|
7988
8021
|
const result = await pool.run({ urlPath });
|
|
7989
|
-
if (result.statusCode < 400) {
|
|
7990
|
-
await pagefindIndex?.addHTMLFile({
|
|
7991
|
-
url: urlPath,
|
|
7992
|
-
content: result.html
|
|
7993
|
-
});
|
|
7994
|
-
}
|
|
7995
8022
|
completedCount++;
|
|
7996
8023
|
if (isTTY()) {
|
|
7997
8024
|
writeProgress(completedCount, paths.length, urlPath);
|
|
@@ -8009,9 +8036,6 @@ var prerender = async ({
|
|
|
8009
8036
|
return result;
|
|
8010
8037
|
})
|
|
8011
8038
|
);
|
|
8012
|
-
const pagefindWriteResult = await pagefindIndex?.writeFiles({
|
|
8013
|
-
outputPath: path21.join(distDir, "pagefind")
|
|
8014
|
-
});
|
|
8015
8039
|
const seconds = ((performance.now() - start) / 1e3).toFixed(1);
|
|
8016
8040
|
const message = `\u2713 finished prerendering ${paths.length} routes in ${seconds} seconds using ${maxThreads} workers`;
|
|
8017
8041
|
if (isTTY()) {
|
|
@@ -8020,10 +8044,38 @@ var prerender = async ({
|
|
|
8020
8044
|
} else {
|
|
8021
8045
|
logger.info(colors7.blue(message));
|
|
8022
8046
|
}
|
|
8023
|
-
if (
|
|
8024
|
-
|
|
8025
|
-
|
|
8047
|
+
if (pagefindIndex) {
|
|
8048
|
+
const pagesToIndex = workerResults.flatMap(
|
|
8049
|
+
({ statusCode, html: html2 }, i) => statusCode < 400 ? { url: paths[i], html: html2 } : []
|
|
8026
8050
|
);
|
|
8051
|
+
const BATCH_SIZE = 40;
|
|
8052
|
+
const pagefindStart = performance.now();
|
|
8053
|
+
for (let offset = 0; offset < pagesToIndex.length; offset += BATCH_SIZE) {
|
|
8054
|
+
const batch = pagesToIndex.slice(offset, offset + BATCH_SIZE);
|
|
8055
|
+
await Promise.all(
|
|
8056
|
+
batch.map(
|
|
8057
|
+
({ url, html: html2 }) => pagefindIndex.addHTMLFile({ url, content: html2 })
|
|
8058
|
+
)
|
|
8059
|
+
);
|
|
8060
|
+
if (isTTY()) {
|
|
8061
|
+
const done = offset + batch.length;
|
|
8062
|
+
writeLine(
|
|
8063
|
+
`pagefind indexing (${done}/${pagesToIndex.length}) ${colors7.dim(batch.at(-1)?.url ?? "")}`
|
|
8064
|
+
);
|
|
8065
|
+
}
|
|
8066
|
+
}
|
|
8067
|
+
if (isTTY()) writeLine("");
|
|
8068
|
+
const { outputPath } = await pagefindIndex.writeFiles({
|
|
8069
|
+
outputPath: path21.join(distDir, "pagefind")
|
|
8070
|
+
});
|
|
8071
|
+
if (outputPath) {
|
|
8072
|
+
const duration = (performance.now() - pagefindStart) / 1e3;
|
|
8073
|
+
logger.info(
|
|
8074
|
+
colors7.blue(
|
|
8075
|
+
`\u2713 pagefind index built in ${duration.toFixed(1)} seconds: ${outputPath}`
|
|
8076
|
+
)
|
|
8077
|
+
);
|
|
8078
|
+
}
|
|
8027
8079
|
}
|
|
8028
8080
|
const redirectUrls = getRedirectUrls(workerResults, config2.basePath);
|
|
8029
8081
|
await generateSitemap({
|
|
@@ -8074,6 +8126,52 @@ var prerender = async ({
|
|
|
8074
8126
|
}
|
|
8075
8127
|
return { workerResults, rewrites };
|
|
8076
8128
|
};
|
|
8129
|
+
var getWorkerScaling = (workersOverride) => {
|
|
8130
|
+
const PER_WORKER_HEAP_LIMIT_MB = 4096;
|
|
8131
|
+
const MAX_WORKERS = 8;
|
|
8132
|
+
const osTotalMb = Math.floor(os.totalmem() / (1024 * 1024));
|
|
8133
|
+
const cgroupMemMb = getContainerMemoryLimitMb();
|
|
8134
|
+
const totalMemMb = cgroupMemMb !== void 0 ? Math.min(cgroupMemMb, osTotalMb) : osTotalMb;
|
|
8135
|
+
const reservedMb = Math.max(2048, Math.floor(totalMemMb * 0.25));
|
|
8136
|
+
const availableForWorkersMb = totalMemMb - reservedMb;
|
|
8137
|
+
const memBasedWorkers = Math.max(
|
|
8138
|
+
1,
|
|
8139
|
+
Math.floor(availableForWorkersMb / PER_WORKER_HEAP_LIMIT_MB)
|
|
8140
|
+
);
|
|
8141
|
+
const cpuBasedWorkers = Math.max(1, Math.floor(os.cpus().length * 0.5));
|
|
8142
|
+
const defaultWorkers = Math.min(
|
|
8143
|
+
memBasedWorkers,
|
|
8144
|
+
cpuBasedWorkers,
|
|
8145
|
+
MAX_WORKERS
|
|
8146
|
+
);
|
|
8147
|
+
const validOverride = workersOverride && workersOverride > 0 ? workersOverride : void 0;
|
|
8148
|
+
const maxThreads = validOverride ?? defaultWorkers;
|
|
8149
|
+
const maxOldGenerationSizeMb = Math.min(
|
|
8150
|
+
PER_WORKER_HEAP_LIMIT_MB,
|
|
8151
|
+
Math.max(512, Math.floor(availableForWorkersMb / maxThreads))
|
|
8152
|
+
);
|
|
8153
|
+
return { maxThreads, maxOldGenerationSizeMb };
|
|
8154
|
+
};
|
|
8155
|
+
var getContainerMemoryLimitMb = () => {
|
|
8156
|
+
const CGROUP_PATHS = [
|
|
8157
|
+
"/sys/fs/cgroup/memory.max",
|
|
8158
|
+
// cgroup v2
|
|
8159
|
+
"/sys/fs/cgroup/memory/memory.limit_in_bytes"
|
|
8160
|
+
// cgroup v1
|
|
8161
|
+
];
|
|
8162
|
+
for (const filePath of CGROUP_PATHS) {
|
|
8163
|
+
try {
|
|
8164
|
+
const raw = readFileSync2(filePath, "utf8").trim();
|
|
8165
|
+
if (raw === "max") return void 0;
|
|
8166
|
+
const bytes = Number(raw);
|
|
8167
|
+
if (!Number.isFinite(bytes) || bytes > 2 ** 50) return void 0;
|
|
8168
|
+
return Math.floor(bytes / (1024 * 1024));
|
|
8169
|
+
} catch (err) {
|
|
8170
|
+
if (err.code !== "ENOENT") throw err;
|
|
8171
|
+
}
|
|
8172
|
+
}
|
|
8173
|
+
return void 0;
|
|
8174
|
+
};
|
|
8077
8175
|
|
|
8078
8176
|
// src/vite/build.ts
|
|
8079
8177
|
var DIST_DIR = "dist";
|
|
@@ -8160,7 +8258,7 @@ var runPrerender = async (options) => {
|
|
|
8160
8258
|
await writeFile5(indexHtml, html, "utf-8");
|
|
8161
8259
|
}
|
|
8162
8260
|
const statusPages = workerResults.flatMap(
|
|
8163
|
-
(r) =>
|
|
8261
|
+
(r) => /^(400|404|500)\.html$/.test(path22.basename(r.outputPath)) ? r.outputPath : []
|
|
8164
8262
|
);
|
|
8165
8263
|
for (const statusPage of statusPages) {
|
|
8166
8264
|
await rename(
|
|
@@ -43,6 +43,7 @@ export declare class OpenIDAuthenticationProvider extends CoreAuthenticationPlug
|
|
|
43
43
|
replace?: boolean;
|
|
44
44
|
}): Promise<void>;
|
|
45
45
|
signIn(_: AuthActionContext, { redirectTo, replace }: AuthActionOptions): Promise<void>;
|
|
46
|
+
private buildUserProfile;
|
|
46
47
|
refreshUserProfile(): Promise<boolean>;
|
|
47
48
|
private authorize;
|
|
48
49
|
getAccessToken(): Promise<string>;
|
|
@@ -39,11 +39,16 @@ export declare const useAuthState: import("zustand").UseBoundStore<Omit<import("
|
|
|
39
39
|
getOptions: () => Partial<import("zustand/middleware").PersistOptions<AuthState, unknown, unknown>>;
|
|
40
40
|
};
|
|
41
41
|
}>;
|
|
42
|
+
export type CustomClaim = string | number | boolean | null | CustomClaimRecord | CustomClaimArray | undefined;
|
|
43
|
+
export interface CustomClaimRecord {
|
|
44
|
+
[key: string]: CustomClaim;
|
|
45
|
+
}
|
|
46
|
+
export type CustomClaimArray = CustomClaim[];
|
|
42
47
|
export interface UserProfile {
|
|
43
48
|
sub: string;
|
|
44
49
|
email: string | undefined;
|
|
45
50
|
emailVerified: boolean;
|
|
46
51
|
name: string | undefined;
|
|
47
52
|
pictureUrl: string | undefined;
|
|
48
|
-
[key: string]:
|
|
53
|
+
[key: string]: CustomClaim;
|
|
49
54
|
}
|
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const Catalog: ({ items, filterCatalogItems, label,
|
|
3
|
-
categoryLabel?: string;
|
|
4
|
-
}) => import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
import type { ApiCatalogPluginOptions } from "./index.js";
|
|
2
|
+
export declare const Catalog: ({ items, filterCatalogItems, label, }: Omit<ApiCatalogPluginOptions, "path">) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { AuthState } from "../../authentication/state.js";
|
|
2
2
|
import type { ZudokuPlugin } from "../../core/plugins.js";
|
|
3
|
-
export declare const getKey: (category: string, tag: string) => string;
|
|
4
3
|
export type ApiCatalogItem = {
|
|
5
4
|
path: string;
|
|
6
5
|
label: string;
|
|
7
6
|
description: string;
|
|
8
7
|
categories: CatalogCategory[];
|
|
8
|
+
version?: string;
|
|
9
|
+
operationCount?: number;
|
|
9
10
|
};
|
|
10
11
|
export type CatalogCategory = {
|
|
11
12
|
label: string;
|
|
@@ -22,10 +23,4 @@ export type CatalogContext = {
|
|
|
22
23
|
auth: AuthState;
|
|
23
24
|
};
|
|
24
25
|
export type FilterCatalogItemsFn = (items: ApiCatalogItem[], { auth }: CatalogContext) => ApiCatalogItem[];
|
|
25
|
-
export declare const apiCatalogPlugin: ({ path, items, label, categories, filterCatalogItems, }:
|
|
26
|
-
path: string;
|
|
27
|
-
label: string;
|
|
28
|
-
categories?: CatalogCategory[];
|
|
29
|
-
items: ApiCatalogItem[];
|
|
30
|
-
filterCatalogItems?: FilterCatalogItemsFn;
|
|
31
|
-
}) => ZudokuPlugin;
|
|
26
|
+
export declare const apiCatalogPlugin: ({ path, items, label, categories, filterCatalogItems, }: ApiCatalogPluginOptions) => ZudokuPlugin;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { type McpServerData } from "./mcp-configs.js";
|
|
1
2
|
export declare const MCPEndpoint: ({ serverUrl, operationPath, summary, data, }: {
|
|
2
3
|
serverUrl?: string;
|
|
3
4
|
operationPath?: string;
|
|
4
|
-
data?:
|
|
5
|
+
data?: McpServerData;
|
|
5
6
|
summary?: string;
|
|
6
7
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type McpServerData = boolean | Record<string, unknown>;
|
|
2
|
+
export interface AuthHeader {
|
|
3
|
+
headerName: string;
|
|
4
|
+
placeholder: string;
|
|
5
|
+
}
|
|
6
|
+
export type AuthType = "none" | "apiKey" | "oauth";
|
|
7
|
+
export declare const getAuthType: (data?: McpServerData) => AuthType;
|
|
8
|
+
export declare const getAuthHeader: (data?: McpServerData) => AuthHeader | undefined;
|
|
9
|
+
export interface McpSubApp {
|
|
10
|
+
id: string;
|
|
11
|
+
label: string;
|
|
12
|
+
supportedAuth: AuthType[];
|
|
13
|
+
}
|
|
14
|
+
export interface McpApp {
|
|
15
|
+
id: string;
|
|
16
|
+
label: string;
|
|
17
|
+
subApps: McpSubApp[];
|
|
18
|
+
}
|
|
19
|
+
export declare const MCP_APPS: McpApp[];
|
|
20
|
+
export declare const getVisibleApps: (authType: AuthType) => McpApp[];
|
|
21
|
+
export declare const getMcpServerName: (data?: McpServerData, summary?: string) => string;
|
|
22
|
+
export declare const getMcpUrl: (serverUrl?: string, operationPath?: string) => string;
|
|
23
|
+
export declare const getClaudeCodeCommand: (name: string, mcpUrl: string, auth?: AuthHeader) => string;
|
|
24
|
+
export declare const getCodexCliCommand: (name: string, mcpUrl: string, auth?: AuthHeader) => string;
|
|
25
|
+
export declare const getCursorConfig: (name: string, mcpUrl: string, auth?: AuthHeader) => string;
|
|
26
|
+
export declare const getVscodeConfig: (name: string, mcpUrl: string, auth?: AuthHeader) => string;
|
|
27
|
+
export declare const getCodexConfig: (name: string, mcpUrl: string, auth?: AuthHeader) => string;
|
|
28
|
+
export declare const getGenericConfig: (name: string, mcpUrl: string, auth?: AuthHeader) => string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { SchemaObject } from "../../../oas/parser/index.js";
|
|
2
|
-
export declare const SchemaView: ({ schema, defaultOpen, cardHeader, embedded, }: {
|
|
2
|
+
export declare const SchemaView: ({ schema, defaultOpen, cardHeader, embedded, hideRootRef, }: {
|
|
3
3
|
schema?: SchemaObject | null;
|
|
4
4
|
defaultOpen?: boolean;
|
|
5
5
|
cardHeader?: React.ReactNode;
|
|
6
6
|
embedded?: boolean;
|
|
7
|
+
hideRootRef?: boolean;
|
|
7
8
|
}) => import("react/jsx-runtime").JSX.Element | undefined;
|
|
@@ -6,4 +6,5 @@ export declare const isCircularRef: (schema: unknown) => schema is string;
|
|
|
6
6
|
export declare const isArrayCircularRef: (schema: SchemaObject) => schema is SchemaObject & {
|
|
7
7
|
items: SchemaObject;
|
|
8
8
|
};
|
|
9
|
+
export declare const getSchemaRefName: (schema?: SchemaObject | null) => string | undefined;
|
|
9
10
|
export declare const extractCircularRefInfo: (ref?: string | SchemaObject) => string | undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
declare
|
|
4
|
-
declare
|
|
5
|
-
declare
|
|
6
|
-
export { HoverCard,
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
declare function HoverCard({ ...props }: React.ComponentProps<typeof HoverCardPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
declare function HoverCardTrigger({ ...props }: React.ComponentProps<typeof HoverCardPrimitive.Trigger>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function HoverCardContent({ className, align, sideOffset, ...props }: React.ComponentProps<typeof HoverCardPrimitive.Content>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export { HoverCard, HoverCardTrigger, HoverCardContent };
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
|
-
import * as React from "react";
|
|
4
3
|
declare const toggleVariants: (props?: ({
|
|
5
4
|
variant?: "default" | "outline" | null | undefined;
|
|
6
5
|
size?: "default" | "sm" | "lg" | null | undefined;
|
|
7
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
|
-
declare
|
|
9
|
-
variant?: "default" | "outline" | null | undefined;
|
|
10
|
-
size?: "default" | "sm" | "lg" | null | undefined;
|
|
11
|
-
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & React.RefAttributes<HTMLButtonElement>>;
|
|
7
|
+
declare function Toggle({ className, variant, size, ...props }: TogglePrimitive.Props & VariantProps<typeof toggleVariants>): import("react/jsx-runtime").JSX.Element;
|
|
12
8
|
export { Toggle, toggleVariants };
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group";
|
|
2
2
|
import type { VariantProps } from "class-variance-authority";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
size?: "default" | "sm" | "lg" | null | undefined;
|
|
11
|
-
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & React.RefAttributes<HTMLButtonElement>>;
|
|
4
|
+
import { Toggle, toggleVariants } from "./Toggle.js";
|
|
5
|
+
declare function ToggleGroup({ className, variant, size, spacing, orientation, children, ...props }: ToggleGroupPrimitive.Props & VariantProps<typeof toggleVariants> & {
|
|
6
|
+
spacing?: number;
|
|
7
|
+
orientation?: "horizontal" | "vertical";
|
|
8
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
declare function ToggleGroupItem({ className, children, variant, size, ...props }: React.ComponentProps<typeof Toggle>): import("react/jsx-runtime").JSX.Element;
|
|
12
10
|
export { ToggleGroup, ToggleGroupItem };
|
|
@@ -34,7 +34,10 @@ If you don't have an Auth0 account, you can sign up for a
|
|
|
34
34
|
- Production: `https://your-site.com/oauth/callback`
|
|
35
35
|
- Preview (wildcard): `https://*.your-domain.com/oauth/callback`
|
|
36
36
|
- Local Development: `http://localhost:3000/oauth/callback`
|
|
37
|
-
- **Allowed Logout URLs**:
|
|
37
|
+
- **Allowed Logout URLs**:
|
|
38
|
+
- Production: `https://your-site.com/oauth/logout-callback`
|
|
39
|
+
- Preview (wildcard): `https://*.your-domain.com/oauth/logout-callback`
|
|
40
|
+
- Local Development: `http://localhost:3000/oauth/logout-callback`
|
|
38
41
|
|
|
39
42
|
- **Allowed Web Origins**:
|
|
40
43
|
- Production: `https://your-site.com`
|
|
@@ -112,8 +115,8 @@ To enable logout for your Auth0 application:
|
|
|
112
115
|
|
|
113
116
|
1. Ensure your **Allowed Logout URLs** are configured in Auth0 (see
|
|
114
117
|
[Configure Auth0 Application](#setup-steps) above)
|
|
115
|
-
2. The logout URL
|
|
116
|
-
production)
|
|
118
|
+
2. The logout URL must use the `/oauth/logout-callback` path (e.g.,
|
|
119
|
+
`https://your-site.com/oauth/logout-callback` for production)
|
|
117
120
|
|
|
118
121
|
For older tenants, you may need to enable **RP-Initiated Logout** in your tenant settings. See the
|
|
119
122
|
[Auth0 logout documentation](https://auth0.com/docs/authenticate/login/logout/log-users-out-of-auth0)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: OpenID Connect (OIDC)
|
|
3
|
+
sidebar_label: OpenID Connect
|
|
4
|
+
description:
|
|
5
|
+
Configure any OpenID Connect compliant identity provider (Okta, Keycloak, Authentik, etc.) as the
|
|
6
|
+
authentication provider for Zudoku.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Zudoku supports any identity provider that implements the
|
|
10
|
+
[OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html) protocol via the generic
|
|
11
|
+
`openid` provider type. This includes Okta, Keycloak, Authentik, Ory, ZITADEL, AWS Cognito, Google
|
|
12
|
+
Identity, and most enterprise IdPs.
|
|
13
|
+
|
|
14
|
+
## Configuration
|
|
15
|
+
|
|
16
|
+
Add the `authentication` property to your [Zudoku configuration](./overview.md):
|
|
17
|
+
|
|
18
|
+
```typescript title="zudoku.config.ts"
|
|
19
|
+
{
|
|
20
|
+
// ...
|
|
21
|
+
authentication: {
|
|
22
|
+
type: "openid",
|
|
23
|
+
clientId: "<your-client-id>",
|
|
24
|
+
issuer: "<the-issuer-url>",
|
|
25
|
+
scopes: ["openid", "profile", "email"], // Optional
|
|
26
|
+
},
|
|
27
|
+
// ...
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Option | Required | Description |
|
|
32
|
+
| ---------- | -------- | -------------------------------------------------------------------------------------------- |
|
|
33
|
+
| `clientId` | Yes | The OAuth client ID issued by your provider. |
|
|
34
|
+
| `issuer` | Yes | The issuer URL. Zudoku discovers endpoints from `<issuer>/.well-known/openid-configuration`. |
|
|
35
|
+
| `scopes` | No | Scopes to request. Defaults to `["openid", "profile", "email"]`. |
|
|
36
|
+
|
|
37
|
+
## Provider Setup
|
|
38
|
+
|
|
39
|
+
Register Zudoku as a public SPA / single page application client in your identity provider and set:
|
|
40
|
+
|
|
41
|
+
- Callback / Redirect URI to `https://your-site.com/oauth/callback`
|
|
42
|
+
- For local development, add `http://localhost:3000/oauth/callback`
|
|
43
|
+
- If your provider supports wildcards, add `https://*.your-domain.com/oauth/callback` for preview
|
|
44
|
+
environments
|
|
45
|
+
- Add your site origin to the list of allowed CORS origins
|
|
46
|
+
- Enable the `Authorization Code` grant with PKCE and the `Refresh Token` grant
|
|
47
|
+
|
|
48
|
+
### Okta
|
|
49
|
+
|
|
50
|
+
1. In the Okta admin console go to **Applications** → **Applications** → **Create App Integration**.
|
|
51
|
+
2. Select **OIDC - OpenID Connect** and **Single Page Application**.
|
|
52
|
+
3. Set **Sign-in redirect URIs** to `https://your-site.com/oauth/callback` (add
|
|
53
|
+
`http://localhost:3000/oauth/callback` for local development).
|
|
54
|
+
4. Under **Assignments**, assign the users or groups that should have access.
|
|
55
|
+
5. After creating the app, copy the **Client ID**. Your issuer is your Okta domain, for example
|
|
56
|
+
`https://your-tenant.okta.com` or a custom authorization server like
|
|
57
|
+
`https://your-tenant.okta.com/oauth2/default`.
|
|
58
|
+
6. Under **Security** → **API** → **Trusted Origins**, add your site origin for both CORS and
|
|
59
|
+
Redirect.
|
|
60
|
+
|
|
61
|
+
```typescript title="zudoku.config.ts"
|
|
62
|
+
{
|
|
63
|
+
authentication: {
|
|
64
|
+
type: "openid",
|
|
65
|
+
clientId: "<your-okta-client-id>",
|
|
66
|
+
issuer: "https://your-tenant.okta.com/oauth2/default",
|
|
67
|
+
scopes: ["openid", "profile", "email"],
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Keycloak
|
|
73
|
+
|
|
74
|
+
Use the realm issuer URL:
|
|
75
|
+
|
|
76
|
+
```typescript title="zudoku.config.ts"
|
|
77
|
+
{
|
|
78
|
+
authentication: {
|
|
79
|
+
type: "openid",
|
|
80
|
+
clientId: "zudoku",
|
|
81
|
+
issuer: "https://keycloak.example.com/realms/<your-realm>",
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
In the realm, create a client with **Client type** `OpenID Connect`, **Access type** `public`, and
|
|
87
|
+
enable **Standard Flow** (Authorization Code).
|
|
88
|
+
|
|
89
|
+
## Verifying the Issuer
|
|
90
|
+
|
|
91
|
+
You can confirm your issuer URL is correct by opening `<issuer>/.well-known/openid-configuration` in
|
|
92
|
+
a browser. It should return a JSON document listing `authorization_endpoint`, `token_endpoint`,
|
|
93
|
+
`userinfo_endpoint`, and `jwks_uri`.
|
|
94
|
+
|
|
95
|
+
## User Profile
|
|
96
|
+
|
|
97
|
+
After sign-in Zudoku calls the provider's
|
|
98
|
+
[UserInfo endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) and reads
|
|
99
|
+
`name`, `email`, `picture`, and `email_verified` from the response. Map these claims in your
|
|
100
|
+
provider if they are not emitted by default.
|
|
101
|
+
|
|
102
|
+
## Troubleshooting
|
|
103
|
+
|
|
104
|
+
- **Discovery fails**: verify `<issuer>/.well-known/openid-configuration` resolves and matches the
|
|
105
|
+
`issuer` value in the document.
|
|
106
|
+
- **CORS errors on token / userinfo**: add your site origin to the provider's allowed origins.
|
|
107
|
+
- **Redirect URI mismatch**: the URI registered with the provider must match the Zudoku origin
|
|
108
|
+
exactly, including protocol and port.
|
|
109
|
+
- **Missing profile fields**: ensure `profile` and `email` scopes are granted and that the provider
|
|
110
|
+
includes `name`, `email`, and `picture` claims in the UserInfo response.
|
|
@@ -15,8 +15,8 @@ authentication provider you use.
|
|
|
15
15
|
|
|
16
16
|
## Authentication Providers
|
|
17
17
|
|
|
18
|
-
Zudoku supports Clerk, Auth0, Supabase, Firebase, Azure B2C, and any OpenID provider
|
|
19
|
-
|
|
18
|
+
Zudoku supports Clerk, Auth0, Supabase, Firebase, Azure B2C, and any OpenID Connect provider
|
|
19
|
+
(including Okta, Keycloak, Authentik, and PingFederate).
|
|
20
20
|
|
|
21
21
|
Not seeing your authentication provider? [Let us know](https://github.com/zuplo/zudoku/issues)
|
|
22
22
|
|
|
@@ -96,6 +96,9 @@ When configuring your OpenID provider, you will need to set the following:
|
|
|
96
96
|
By default, the scopes "openid", "profile", and "email" are requested. You can customize these by
|
|
97
97
|
providing your own array of scopes.
|
|
98
98
|
|
|
99
|
+
For provider-specific guides (Okta, Keycloak, etc.), see the
|
|
100
|
+
[OpenID Connect setup page](./authentication-openid.md).
|
|
101
|
+
|
|
99
102
|
### Firebase
|
|
100
103
|
|
|
101
104
|
For Firebase authentication, you will need your Firebase project configuration. You can find this in
|
|
@@ -30,6 +30,18 @@ const config: ZudokuConfig = {
|
|
|
30
30
|
};
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
When `basePath` is set, Zudoku writes the site into `dist/<basePath>/`. Upload that nested directory
|
|
34
|
+
as your Pages artifact, not `dist/` itself. For example in a GitHub Actions workflow:
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
- uses: actions/upload-pages-artifact@v3
|
|
38
|
+
with:
|
|
39
|
+
path: dist/your-repo
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Uploading the outer `dist/` would nest the site under `/your-repo/your-repo/` and every asset
|
|
43
|
+
would 404.
|
|
44
|
+
|
|
33
45
|
## Accurate Last Modified Dates
|
|
34
46
|
|
|
35
47
|
If you have enabled the [`showLastModified`](/docs/configuration/docs#showlastmodified) option,
|