zudoku 0.18.8 → 0.19.0

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.
Files changed (43) hide show
  1. package/dist/app/main.js +2 -1
  2. package/dist/app/main.js.map +1 -1
  3. package/dist/config/validators/validate.d.ts +370 -0
  4. package/dist/config/validators/validate.js +12 -0
  5. package/dist/config/validators/validate.js.map +1 -1
  6. package/dist/lib/authentication/providers/openid.js +7 -2
  7. package/dist/lib/authentication/providers/openid.js.map +1 -1
  8. package/dist/lib/oas/graphql/index.js +0 -1
  9. package/dist/lib/oas/graphql/index.js.map +1 -1
  10. package/dist/lib/plugins/api-catalog/Catalog.d.ts +6 -0
  11. package/dist/lib/plugins/api-catalog/Catalog.js +29 -0
  12. package/dist/lib/plugins/api-catalog/Catalog.js.map +1 -0
  13. package/dist/lib/plugins/api-catalog/index.d.ts +23 -0
  14. package/dist/lib/plugins/api-catalog/index.js +15 -0
  15. package/dist/lib/plugins/api-catalog/index.js.map +1 -0
  16. package/dist/lib/plugins/openapi/playground/PathParams.js +1 -1
  17. package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
  18. package/dist/vite/plugin-api.js +44 -1
  19. package/dist/vite/plugin-api.js.map +1 -1
  20. package/dist/vite/plugin-component.js +1 -0
  21. package/dist/vite/plugin-component.js.map +1 -1
  22. package/lib/{OperationList-DzE32oyS.js → OperationList-DT5Fu9bC.js} +2 -2
  23. package/lib/{OperationList-DzE32oyS.js.map → OperationList-DT5Fu9bC.js.map} +1 -1
  24. package/lib/assets/{worker-CyxLedqF.js → worker-C_2va8B8.js} +1 -2
  25. package/lib/assets/{worker-CyxLedqF.js.map → worker-C_2va8B8.js.map} +1 -1
  26. package/lib/{createServer-DTiCfoql.js → createServer-BCAHdrpE.js} +1 -2
  27. package/lib/{createServer-DTiCfoql.js.map → createServer-BCAHdrpE.js.map} +1 -1
  28. package/lib/{index-NNCc1BSK.js → index-CBctPUfP.js} +4 -3
  29. package/lib/index-CBctPUfP.js.map +1 -0
  30. package/lib/zudoku.auth-openid.js +42 -37
  31. package/lib/zudoku.auth-openid.js.map +1 -1
  32. package/lib/zudoku.openapi-worker.js +1 -1
  33. package/lib/zudoku.plugin-api-catalog.js +121 -0
  34. package/lib/zudoku.plugin-api-catalog.js.map +1 -0
  35. package/lib/zudoku.plugin-openapi.js +1 -1
  36. package/package.json +6 -2
  37. package/src/app/main.tsx +5 -1
  38. package/src/lib/authentication/providers/openid.tsx +7 -2
  39. package/src/lib/oas/graphql/index.ts +0 -1
  40. package/src/lib/plugins/api-catalog/Catalog.tsx +124 -0
  41. package/src/lib/plugins/api-catalog/index.tsx +50 -0
  42. package/src/lib/plugins/openapi/playground/PathParams.tsx +1 -0
  43. package/lib/index-NNCc1BSK.js.map +0 -1
@@ -2,7 +2,7 @@ const o = () => {
2
2
  const r = new SharedWorker(
3
3
  new URL(
4
4
  /* @vite-ignore */
5
- "./assets/worker-CyxLedqF.js",
5
+ "./assets/worker-C_2va8B8.js",
6
6
  import.meta.url
7
7
  ),
8
8
  { type: "module" }
@@ -0,0 +1,121 @@
1
+ import { j as e } from "./jsx-runtime-B6kdoens.js";
2
+ import { s as n } from "./index-LNp6rxyU.js";
3
+ import { Fragment as x } from "react";
4
+ import { Head as f, Link as h } from "./zudoku.components.js";
5
+ import { M as u } from "./Markdown-ievDDhFT.js";
6
+ import { u as j } from "./useExposedProps-CTPtylCV.js";
7
+ const b = ({
8
+ items: l,
9
+ categories: i,
10
+ label: o = "API Library"
11
+ }) => {
12
+ const { searchParams: c, setSearchParams: m } = j(), t = c.get("category");
13
+ return /* @__PURE__ */ e.jsxs("section", { className: "pt-[--padding-content-top] pb-[--padding-content-bottom]", children: [
14
+ /* @__PURE__ */ e.jsx(f, { children: /* @__PURE__ */ e.jsx("title", { children: o }) }),
15
+ /* @__PURE__ */ e.jsxs("div", { className: "grid grid-cols-12 gap-12", children: [
16
+ /* @__PURE__ */ e.jsx("div", { className: "flex flex-col gap-4 col-span-3 px-4 not-prose sticky top-48", children: /* @__PURE__ */ e.jsx("div", { className: "justify-between", children: i.map((s, a) => /* @__PURE__ */ e.jsxs(x, { children: [
17
+ /* @__PURE__ */ e.jsxs("div", { className: "flex justify-between mb-2.5", children: [
18
+ /* @__PURE__ */ e.jsx("span", { className: "font-medium text-sm", children: s.label }),
19
+ a === 0 && t && /* @__PURE__ */ e.jsx(
20
+ "button",
21
+ {
22
+ type: "button",
23
+ className: "text-end text-sm mr-8 text-foreground/60 hover:text-foreground",
24
+ onClick: () => m({}),
25
+ children: "Clear"
26
+ }
27
+ )
28
+ ] }),
29
+ /* @__PURE__ */ e.jsx("ul", { className: "space-y-1 [&>li]:py-2", children: Array.from(
30
+ new Set(
31
+ s.tags.map((r) => ({
32
+ tag: r,
33
+ count: l.filter(
34
+ (d) => d.categories.find((p) => p.tags.includes(r))
35
+ ).length
36
+ })).map(({ tag: r, count: d }) => /* @__PURE__ */ e.jsxs(
37
+ "li",
38
+ {
39
+ className: `flex px-4 rounded-lg -translate-x-4 justify-between text-sm cursor-pointer hover:text-primary transition ${n(r) === t ? "font-medium bg-border/30 rounded" : ""}`,
40
+ onClick: () => m({
41
+ category: n(s.label + " " + r)
42
+ }),
43
+ children: [
44
+ /* @__PURE__ */ e.jsx("span", { children: r }),
45
+ /* @__PURE__ */ e.jsx(
46
+ "span",
47
+ {
48
+ className: `flex items-center justify-center border rounded-md w-8 text-xs font-semibold ${n(r) === t ? "bg-primary border-primary text-primary-foreground" : ""}`,
49
+ children: d
50
+ }
51
+ )
52
+ ]
53
+ },
54
+ n(s.label + " " + r)
55
+ ))
56
+ )
57
+ ) })
58
+ ] }, s.label)) }) }),
59
+ /* @__PURE__ */ e.jsxs("div", { className: "col-span-9", children: [
60
+ /* @__PURE__ */ e.jsx("h3", { className: "mt-0 text-2xl font-bold mb-4", children: o }),
61
+ /* @__PURE__ */ e.jsx("div", { className: "grid grid-cols-2 gap-4", children: l.filter(
62
+ (s) => !t || s.categories.find(
63
+ (a) => a.tags.find(
64
+ (r) => n(a.label + " " + r) === t
65
+ )
66
+ )
67
+ ).map((s, a) => /* @__PURE__ */ e.jsx(
68
+ h,
69
+ {
70
+ to: {
71
+ pathname: `/${s.path}`,
72
+ search: t ? `category=${t}` : ""
73
+ },
74
+ className: "no-underline hover:!text-foreground",
75
+ children: /* @__PURE__ */ e.jsxs(
76
+ "div",
77
+ {
78
+ className: "border h-full rounded p-4 flex flex-col gap-2 cursor-pointer hover:bg-border/20 font-normal",
79
+ children: [
80
+ /* @__PURE__ */ e.jsx("span", { className: "font-semibold", children: s.label }),
81
+ /* @__PURE__ */ e.jsx(
82
+ u,
83
+ {
84
+ className: "text-sm whitespace-pre-wrap mb-6 line-clamp-2",
85
+ content: s.description
86
+ }
87
+ )
88
+ ]
89
+ },
90
+ a
91
+ )
92
+ },
93
+ s.path
94
+ )) })
95
+ ] })
96
+ ] })
97
+ ] });
98
+ }, k = ({
99
+ navigationId: l,
100
+ items: i,
101
+ label: o,
102
+ categories: c
103
+ }) => ({
104
+ getRoutes: () => [
105
+ {
106
+ path: l,
107
+ element: /* @__PURE__ */ e.jsx(
108
+ b,
109
+ {
110
+ label: o,
111
+ items: i,
112
+ categories: c ?? []
113
+ }
114
+ )
115
+ }
116
+ ]
117
+ });
118
+ export {
119
+ k as apiCatalogPlugin
120
+ };
121
+ //# sourceMappingURL=zudoku.plugin-api-catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zudoku.plugin-api-catalog.js","sources":["../src/lib/plugins/api-catalog/Catalog.tsx","../src/lib/plugins/api-catalog/index.tsx"],"sourcesContent":["import slugify from \"@sindresorhus/slugify\";\nimport { Fragment } from \"react\";\nimport { Head, Link } from \"zudoku/components\";\nimport { Markdown } from \"../../components/Markdown.js\";\nimport { useExposedProps } from \"../../util/useExposedProps.js\";\nimport type { ApiCatalogItem, CatalogCategory } from \"./index.js\";\n\nexport const Catalog = ({\n items,\n categories,\n label = \"API Library\",\n}: {\n label: string;\n items: ApiCatalogItem[];\n categories: CatalogCategory[];\n}) => {\n const { searchParams, setSearchParams } = useExposedProps();\n const activeCategory = searchParams.get(\"category\");\n return (\n <section className=\"pt-[--padding-content-top] pb-[--padding-content-bottom]\">\n <Head>\n <title>{label}</title>\n </Head>\n <div className=\"grid grid-cols-12 gap-12\">\n <div className=\"flex flex-col gap-4 col-span-3 px-4 not-prose sticky top-48\">\n <div className=\"justify-between\">\n {categories.map((category, idx) => (\n <Fragment key={category.label}>\n <div className=\"flex justify-between mb-2.5\">\n <span className=\"font-medium text-sm\">{category.label}</span>\n {idx === 0 && activeCategory && (\n <button\n type=\"button\"\n className=\"text-end text-sm mr-8 text-foreground/60 hover:text-foreground\"\n onClick={() => setSearchParams({})}\n >\n Clear\n </button>\n )}\n </div>\n <ul className=\"space-y-1 [&>li]:py-2\">\n {Array.from(\n new Set(\n category.tags\n .map((tag) => ({\n tag,\n count: items.filter((api) =>\n api.categories.find((c) => c.tags.includes(tag)),\n ).length,\n }))\n .map(({ tag, count }) => (\n <li\n key={slugify(category.label + \" \" + tag)}\n className={`flex px-4 rounded-lg -translate-x-4 justify-between text-sm cursor-pointer hover:text-primary transition ${\n slugify(tag) === activeCategory\n ? \"font-medium bg-border/30 rounded\"\n : \"\"\n }`}\n onClick={() =>\n setSearchParams({\n category: slugify(category.label + \" \" + tag),\n })\n }\n >\n <span>{tag}</span>\n <span\n className={`flex items-center justify-center border rounded-md w-8 text-xs font-semibold ${\n slugify(tag) === activeCategory\n ? \"bg-primary border-primary text-primary-foreground\"\n : \"\"\n }`}\n >\n {count}\n </span>\n </li>\n )),\n ),\n )}\n </ul>\n </Fragment>\n ))}\n </div>\n </div>\n <div className=\"col-span-9\">\n <h3 className=\"mt-0 text-2xl font-bold mb-4\">{label}</h3>\n\n <div className=\"grid grid-cols-2 gap-4\">\n {items\n .filter(\n (api) =>\n !activeCategory ||\n api.categories.find((c) =>\n c.tags.find(\n (t) => slugify(c.label + \" \" + t) === activeCategory,\n ),\n ),\n )\n .map((api, i) => (\n <Link\n to={{\n pathname: `/${api.path}`,\n search: activeCategory ? `category=${activeCategory}` : \"\",\n }}\n className=\"no-underline hover:!text-foreground\"\n key={api.path}\n >\n <div\n className=\"border h-full rounded p-4 flex flex-col gap-2 cursor-pointer hover:bg-border/20 font-normal\"\n key={i}\n >\n <span className=\"font-semibold\">{api.label}</span>\n <Markdown\n className=\"text-sm whitespace-pre-wrap mb-6 line-clamp-2\"\n content={api.description}\n />\n </div>\n </Link>\n ))}\n </div>\n </div>\n </div>\n </section>\n );\n};\n","import type { ZudokuPlugin } from \"../../core/plugins.js\";\nimport { Catalog } from \"./Catalog.js\";\n\nexport type ApiCatalogItem = {\n path: string;\n label: string;\n description: string;\n categories: CatalogCategory[];\n};\n\nexport type CatalogCategory = {\n label: string;\n tags: string[];\n};\n\nexport type ApiCatalogPluginOptions = {\n navigationId: string;\n label: string;\n categories?: CatalogCategory[];\n items: ApiCatalogItem[];\n};\n\nexport const apiCatalogPlugin = ({\n navigationId,\n items,\n label,\n categories,\n}: {\n navigationId: string;\n label: string;\n categories?: CatalogCategory[];\n items: ApiCatalogItem[];\n}): ZudokuPlugin => {\n return {\n getRoutes: () => {\n return [\n {\n path: navigationId,\n element: (\n <Catalog\n label={label}\n items={items}\n categories={categories ?? []}\n />\n ),\n },\n ];\n },\n };\n};\n"],"names":["Catalog","items","categories","label","searchParams","setSearchParams","useExposedProps","activeCategory","jsxs","jsx","Head","category","idx","Fragment","tag","api","c","count","slugify","t","i","Link","Markdown","apiCatalogPlugin","navigationId"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import "./jsx-runtime-B6kdoens.js";
2
- import { o as a } from "./index-NNCc1BSK.js";
2
+ import { o as a } from "./index-CBctPUfP.js";
3
3
  import "./utils-DcpDOncX.js";
4
4
  import "lucide-react";
5
5
  import "./hook-hEqe7fPB.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.18.8",
3
+ "version": "0.19.0",
4
4
  "type": "module",
5
5
  "homepage": "https://zudoku.dev",
6
6
  "repository": {
@@ -54,6 +54,10 @@
54
54
  "import": "./lib/zudoku.plugin-api-keys.js",
55
55
  "types": "./dist/lib/plugins/api-keys/index.d.ts"
56
56
  },
57
+ "./plugins/api-catalog": {
58
+ "import": "./lib/zudoku.plugin-api-catalog.js",
59
+ "types": "./dist/lib/plugins/api-catalog/index.d.ts"
60
+ },
57
61
  "./plugins/markdown": {
58
62
  "import": "./lib/zudoku.plugin-markdown.js",
59
63
  "types": "./dist/lib/plugins/markdown/index.d.ts"
@@ -149,7 +153,7 @@
149
153
  "@types/react": "18.3.11",
150
154
  "@types/react-dom": "18.3.1",
151
155
  "@vitejs/plugin-react": "4.3.4",
152
- "@zudoku/config": "0.18.8",
156
+ "@zudoku/config": "0.19.0",
153
157
  "@zudoku/httpsnippet": "10.0.9",
154
158
  "@zudoku/react-helmet-async": "2.0.4",
155
159
  "autoprefixer": "10.4.20",
package/src/app/main.tsx CHANGED
@@ -1,6 +1,9 @@
1
1
  import { type RouteObject } from "react-router-dom";
2
2
  import { configuredApiKeysPlugin } from "virtual:zudoku-api-keys-plugin";
3
- import { configuredApiPlugins } from "virtual:zudoku-api-plugins";
3
+ import {
4
+ configuredApiCatalogPlugins,
5
+ configuredApiPlugins,
6
+ } from "virtual:zudoku-api-plugins";
4
7
  import { configuredAuthProvider } from "virtual:zudoku-auth";
5
8
  import { configuredCustomPagesPlugin } from "virtual:zudoku-custom-pages-plugin";
6
9
  import { configuredDocsPlugins } from "virtual:zudoku-docs-plugins";
@@ -57,6 +60,7 @@ export const convertZudokuConfigToOptions = (
57
60
  ...(configuredRedirectPlugin ? [configuredRedirectPlugin] : []),
58
61
  ...(configuredApiKeysPlugin ? [configuredApiKeysPlugin] : []),
59
62
  ...(configuredCustomPagesPlugin ? [configuredCustomPagesPlugin] : []),
63
+ ...configuredApiCatalogPlugins,
60
64
  ...(configuredAuthProvider?.getAuthenticationPlugin
61
65
  ? [configuredAuthProvider.getAuthenticationPlugin()]
62
66
  : []),
@@ -203,9 +203,14 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
203
203
  }
204
204
  const tokenState = providerData as TokenState;
205
205
 
206
- if (tokenState.expiresOn < new Date()) {
206
+ if (new Date(tokenState.expiresOn) < new Date()) {
207
207
  if (!tokenState.refreshToken) {
208
- await this.signIn();
208
+ useAuthState.setState({
209
+ isAuthenticated: false,
210
+ isPending: false,
211
+ profile: null,
212
+ providerData: null,
213
+ });
209
214
  return "";
210
215
  }
211
216
 
@@ -443,7 +443,6 @@ const SchemaSource = builder.enumType("SchemaType", {
443
443
 
444
444
  builder.queryType({
445
445
  fields: (t) => ({
446
- // https://tan-cow-main-bce8a06.d2.zuplo.dev/openapi
447
446
  schema: t.field({
448
447
  type: Schema,
449
448
  args: {
@@ -0,0 +1,124 @@
1
+ import slugify from "@sindresorhus/slugify";
2
+ import { Fragment } from "react";
3
+ import { Head, Link } from "zudoku/components";
4
+ import { Markdown } from "../../components/Markdown.js";
5
+ import { useExposedProps } from "../../util/useExposedProps.js";
6
+ import type { ApiCatalogItem, CatalogCategory } from "./index.js";
7
+
8
+ export const Catalog = ({
9
+ items,
10
+ categories,
11
+ label = "API Library",
12
+ }: {
13
+ label: string;
14
+ items: ApiCatalogItem[];
15
+ categories: CatalogCategory[];
16
+ }) => {
17
+ const { searchParams, setSearchParams } = useExposedProps();
18
+ const activeCategory = searchParams.get("category");
19
+ return (
20
+ <section className="pt-[--padding-content-top] pb-[--padding-content-bottom]">
21
+ <Head>
22
+ <title>{label}</title>
23
+ </Head>
24
+ <div className="grid grid-cols-12 gap-12">
25
+ <div className="flex flex-col gap-4 col-span-3 px-4 not-prose sticky top-48">
26
+ <div className="justify-between">
27
+ {categories.map((category, idx) => (
28
+ <Fragment key={category.label}>
29
+ <div className="flex justify-between mb-2.5">
30
+ <span className="font-medium text-sm">{category.label}</span>
31
+ {idx === 0 && activeCategory && (
32
+ <button
33
+ type="button"
34
+ className="text-end text-sm mr-8 text-foreground/60 hover:text-foreground"
35
+ onClick={() => setSearchParams({})}
36
+ >
37
+ Clear
38
+ </button>
39
+ )}
40
+ </div>
41
+ <ul className="space-y-1 [&>li]:py-2">
42
+ {Array.from(
43
+ new Set(
44
+ category.tags
45
+ .map((tag) => ({
46
+ tag,
47
+ count: items.filter((api) =>
48
+ api.categories.find((c) => c.tags.includes(tag)),
49
+ ).length,
50
+ }))
51
+ .map(({ tag, count }) => (
52
+ <li
53
+ key={slugify(category.label + " " + tag)}
54
+ className={`flex px-4 rounded-lg -translate-x-4 justify-between text-sm cursor-pointer hover:text-primary transition ${
55
+ slugify(tag) === activeCategory
56
+ ? "font-medium bg-border/30 rounded"
57
+ : ""
58
+ }`}
59
+ onClick={() =>
60
+ setSearchParams({
61
+ category: slugify(category.label + " " + tag),
62
+ })
63
+ }
64
+ >
65
+ <span>{tag}</span>
66
+ <span
67
+ className={`flex items-center justify-center border rounded-md w-8 text-xs font-semibold ${
68
+ slugify(tag) === activeCategory
69
+ ? "bg-primary border-primary text-primary-foreground"
70
+ : ""
71
+ }`}
72
+ >
73
+ {count}
74
+ </span>
75
+ </li>
76
+ )),
77
+ ),
78
+ )}
79
+ </ul>
80
+ </Fragment>
81
+ ))}
82
+ </div>
83
+ </div>
84
+ <div className="col-span-9">
85
+ <h3 className="mt-0 text-2xl font-bold mb-4">{label}</h3>
86
+
87
+ <div className="grid grid-cols-2 gap-4">
88
+ {items
89
+ .filter(
90
+ (api) =>
91
+ !activeCategory ||
92
+ api.categories.find((c) =>
93
+ c.tags.find(
94
+ (t) => slugify(c.label + " " + t) === activeCategory,
95
+ ),
96
+ ),
97
+ )
98
+ .map((api, i) => (
99
+ <Link
100
+ to={{
101
+ pathname: `/${api.path}`,
102
+ search: activeCategory ? `category=${activeCategory}` : "",
103
+ }}
104
+ className="no-underline hover:!text-foreground"
105
+ key={api.path}
106
+ >
107
+ <div
108
+ className="border h-full rounded p-4 flex flex-col gap-2 cursor-pointer hover:bg-border/20 font-normal"
109
+ key={i}
110
+ >
111
+ <span className="font-semibold">{api.label}</span>
112
+ <Markdown
113
+ className="text-sm whitespace-pre-wrap mb-6 line-clamp-2"
114
+ content={api.description}
115
+ />
116
+ </div>
117
+ </Link>
118
+ ))}
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </section>
123
+ );
124
+ };
@@ -0,0 +1,50 @@
1
+ import type { ZudokuPlugin } from "../../core/plugins.js";
2
+ import { Catalog } from "./Catalog.js";
3
+
4
+ export type ApiCatalogItem = {
5
+ path: string;
6
+ label: string;
7
+ description: string;
8
+ categories: CatalogCategory[];
9
+ };
10
+
11
+ export type CatalogCategory = {
12
+ label: string;
13
+ tags: string[];
14
+ };
15
+
16
+ export type ApiCatalogPluginOptions = {
17
+ navigationId: string;
18
+ label: string;
19
+ categories?: CatalogCategory[];
20
+ items: ApiCatalogItem[];
21
+ };
22
+
23
+ export const apiCatalogPlugin = ({
24
+ navigationId,
25
+ items,
26
+ label,
27
+ categories,
28
+ }: {
29
+ navigationId: string;
30
+ label: string;
31
+ categories?: CatalogCategory[];
32
+ items: ApiCatalogItem[];
33
+ }): ZudokuPlugin => {
34
+ return {
35
+ getRoutes: () => {
36
+ return [
37
+ {
38
+ path: navigationId,
39
+ element: (
40
+ <Catalog
41
+ label={label}
42
+ items={items}
43
+ categories={categories ?? []}
44
+ />
45
+ ),
46
+ },
47
+ ];
48
+ },
49
+ };
50
+ };
@@ -50,6 +50,7 @@ export const PathParams = ({
50
50
  render={({ field }) => (
51
51
  <Input
52
52
  {...field}
53
+ required
53
54
  placeholder="Enter value"
54
55
  className="border-0 shadow-none ring-0 font-mono text-xs"
55
56
  />