zudoku 0.0.0-f9d5b02 → 0.0.0-fa903e7

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 (183) hide show
  1. package/dist/app/entry.client.js +14 -0
  2. package/dist/app/entry.client.js.map +1 -1
  3. package/dist/cli/common/logger.js +9 -0
  4. package/dist/cli/common/logger.js.map +1 -1
  5. package/dist/config/validators/validate.d.ts +31 -11
  6. package/dist/config/validators/validate.js +7 -1
  7. package/dist/config/validators/validate.js.map +1 -1
  8. package/dist/lib/authentication/AuthenticationPlugin.d.ts +4 -2
  9. package/dist/lib/authentication/AuthenticationPlugin.js +3 -0
  10. package/dist/lib/authentication/AuthenticationPlugin.js.map +1 -1
  11. package/dist/lib/authentication/authentication.d.ts +1 -1
  12. package/dist/lib/authentication/hook.d.ts +5 -4
  13. package/dist/lib/authentication/hook.js +1 -3
  14. package/dist/lib/authentication/hook.js.map +1 -1
  15. package/dist/lib/authentication/providers/auth0.js +2 -2
  16. package/dist/lib/authentication/providers/auth0.js.map +1 -1
  17. package/dist/lib/authentication/providers/openid.d.ts +0 -1
  18. package/dist/lib/authentication/providers/openid.js +11 -26
  19. package/dist/lib/authentication/providers/openid.js.map +1 -1
  20. package/dist/lib/authentication/state.d.ts +25 -4
  21. package/dist/lib/authentication/state.js +28 -5
  22. package/dist/lib/authentication/state.js.map +1 -1
  23. package/dist/lib/components/Bootstrap.js +9 -5
  24. package/dist/lib/components/Bootstrap.js.map +1 -1
  25. package/dist/lib/components/Header.js +12 -4
  26. package/dist/lib/components/Header.js.map +1 -1
  27. package/dist/lib/components/Layout.js +12 -4
  28. package/dist/lib/components/Layout.js.map +1 -1
  29. package/dist/lib/components/MobileTopNavigation.js +5 -7
  30. package/dist/lib/components/MobileTopNavigation.js.map +1 -1
  31. package/dist/lib/components/SyntaxHighlight.js +2 -2
  32. package/dist/lib/components/SyntaxHighlight.js.map +1 -1
  33. package/dist/lib/components/ThemeSwitch.js +5 -3
  34. package/dist/lib/components/ThemeSwitch.js.map +1 -1
  35. package/dist/lib/components/TopNavigation.d.ts +2 -0
  36. package/dist/lib/components/TopNavigation.js +13 -7
  37. package/dist/lib/components/TopNavigation.js.map +1 -1
  38. package/dist/lib/components/index.d.ts +11 -1
  39. package/dist/lib/core/plugins.d.ts +6 -0
  40. package/dist/lib/core/plugins.js.map +1 -1
  41. package/dist/lib/oas/parser/upgrade/index.d.ts +2 -2
  42. package/dist/lib/oas/parser/upgrade/index.js +3 -20
  43. package/dist/lib/oas/parser/upgrade/index.js.map +1 -1
  44. package/dist/lib/plugins/api-keys/index.js +3 -0
  45. package/dist/lib/plugins/api-keys/index.js.map +1 -1
  46. package/dist/lib/plugins/markdown/MdxPage.d.ts +9 -1
  47. package/dist/lib/plugins/markdown/MdxPage.js +14 -1
  48. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  49. package/dist/lib/plugins/markdown/index.js +1 -1
  50. package/dist/lib/plugins/markdown/index.js.map +1 -1
  51. package/dist/lib/plugins/openapi/ColorizedParam.js +2 -2
  52. package/dist/lib/plugins/openapi/ColorizedParam.js.map +1 -1
  53. package/dist/lib/plugins/openapi/client/GraphQLClient.js +12 -0
  54. package/dist/lib/plugins/openapi/client/GraphQLClient.js.map +1 -1
  55. package/dist/lib/plugins/openapi/client/useCreateQuery.d.ts +1 -1
  56. package/dist/lib/plugins/openapi/client/useCreateQuery.js +4 -2
  57. package/dist/lib/plugins/openapi/client/useCreateQuery.js.map +1 -1
  58. package/dist/lib/plugins/openapi/interfaces.d.ts +1 -1
  59. package/dist/lib/plugins/openapi/post-processors/removeExtensions.d.ts +6 -0
  60. package/dist/lib/plugins/openapi/post-processors/removeExtensions.js +14 -0
  61. package/dist/lib/plugins/openapi/post-processors/removeExtensions.js.map +1 -0
  62. package/dist/lib/plugins/openapi/post-processors/removeExtensions.test.d.ts +1 -0
  63. package/dist/lib/plugins/openapi/post-processors/removeExtensions.test.js +125 -0
  64. package/dist/lib/plugins/openapi/post-processors/removeExtensions.test.js.map +1 -0
  65. package/dist/lib/plugins/openapi/post-processors/removePaths.d.ts +11 -0
  66. package/dist/lib/plugins/openapi/post-processors/removePaths.js +33 -0
  67. package/dist/lib/plugins/openapi/post-processors/removePaths.js.map +1 -0
  68. package/dist/lib/plugins/openapi/post-processors/removePaths.test.d.ts +1 -0
  69. package/dist/lib/plugins/openapi/post-processors/removePaths.test.js +104 -0
  70. package/dist/lib/plugins/openapi/post-processors/removePaths.test.js.map +1 -0
  71. package/dist/lib/plugins/openapi/post-processors/traverse.d.ts +1 -0
  72. package/dist/lib/plugins/openapi/post-processors/traverse.js +2 -0
  73. package/dist/lib/plugins/openapi/post-processors/traverse.js.map +1 -0
  74. package/dist/lib/plugins/openapi/schema/SchemaView.js.map +1 -1
  75. package/dist/lib/util/traverse.d.ts +2 -0
  76. package/dist/lib/util/traverse.js +18 -0
  77. package/dist/lib/util/traverse.js.map +1 -0
  78. package/dist/vite/config.js +12 -0
  79. package/dist/vite/config.js.map +1 -1
  80. package/dist/vite/config.test.js +3 -4
  81. package/dist/vite/config.test.js.map +1 -1
  82. package/dist/vite/output.js +20 -0
  83. package/dist/vite/output.js.map +1 -1
  84. package/dist/vite/plugin-api.js +23 -19
  85. package/dist/vite/plugin-api.js.map +1 -1
  86. package/dist/vite/plugin-component.js +14 -19
  87. package/dist/vite/plugin-component.js.map +1 -1
  88. package/dist/vite/plugin-docs.test.js +15 -23
  89. package/dist/vite/plugin-docs.test.js.map +1 -1
  90. package/dist/vite/plugin-mdx.js +10 -3
  91. package/dist/vite/plugin-mdx.js.map +1 -1
  92. package/dist/vite/remarkStaticGeneration.js +5 -5
  93. package/dist/vite/remarkStaticGeneration.js.map +1 -1
  94. package/dist/zuplo/with-zuplo.d.ts +3 -0
  95. package/dist/zuplo/with-zuplo.js +28 -0
  96. package/dist/zuplo/with-zuplo.js.map +1 -0
  97. package/lib/AuthenticationPlugin-D0Em0SwR.js +59 -0
  98. package/lib/AuthenticationPlugin-D0Em0SwR.js.map +1 -0
  99. package/lib/{Markdown-BorQdbxW.js → Markdown-ievDDhFT.js} +2 -2
  100. package/lib/{Markdown-BorQdbxW.js.map → Markdown-ievDDhFT.js.map} +1 -1
  101. package/lib/MdxPage-B2FpJ9KC.js +183 -0
  102. package/lib/MdxPage-B2FpJ9KC.js.map +1 -0
  103. package/lib/{OperationList-KshJrrLL.js → OperationList-BkNQEsNs.js} +460 -458
  104. package/lib/OperationList-BkNQEsNs.js.map +1 -0
  105. package/lib/{Select-DP74t8Yy.js → Select-O9ZM3ZgX.js} +2 -2
  106. package/lib/{Select-DP74t8Yy.js.map → Select-O9ZM3ZgX.js.map} +1 -1
  107. package/lib/{SlotletProvider-D2v6rJy1.js → SlotletProvider-DyomlzGx.js} +2 -2
  108. package/lib/{SlotletProvider-D2v6rJy1.js.map → SlotletProvider-DyomlzGx.js.map} +1 -1
  109. package/lib/{SyntaxHighlight-CBmwwKoM.js → SyntaxHighlight-DkLOsjHS.js} +2 -2
  110. package/lib/{SyntaxHighlight-CBmwwKoM.js.map → SyntaxHighlight-DkLOsjHS.js.map} +1 -1
  111. package/lib/assets/{worker-CPsGZsve.js → worker-BHClFO3A.js} +434 -438
  112. package/lib/assets/worker-BHClFO3A.js.map +1 -0
  113. package/lib/{createServer-DK-g7kbB.js → createServer-CpJlUPtn.js} +4457 -5247
  114. package/lib/createServer-CpJlUPtn.js.map +1 -0
  115. package/lib/{hook-Diu0rqp-.js → hook-hEqe7fPB.js} +12 -14
  116. package/lib/{hook-Diu0rqp-.js.map → hook-hEqe7fPB.js.map} +1 -1
  117. package/lib/{index-BcesIHH4.js → index-C7SaIME0.js} +54 -50
  118. package/lib/index-C7SaIME0.js.map +1 -0
  119. package/lib/object_hash-CvlLgU-M.js +785 -0
  120. package/lib/object_hash-CvlLgU-M.js.map +1 -0
  121. package/lib/post-processors/removeExtensions.js +11 -0
  122. package/lib/post-processors/removeExtensions.js.map +1 -0
  123. package/lib/post-processors/removePaths.js +28 -0
  124. package/lib/post-processors/removePaths.js.map +1 -0
  125. package/lib/post-processors/traverse.js +12 -0
  126. package/lib/post-processors/traverse.js.map +1 -0
  127. package/lib/state-tsXBLONe.js +203 -0
  128. package/lib/state-tsXBLONe.js.map +1 -0
  129. package/lib/zudoku.auth-auth0.js +9 -8
  130. package/lib/zudoku.auth-auth0.js.map +1 -1
  131. package/lib/zudoku.auth-clerk.js +2 -2
  132. package/lib/zudoku.auth-openid.js +119 -133
  133. package/lib/zudoku.auth-openid.js.map +1 -1
  134. package/lib/zudoku.components.js +596 -530
  135. package/lib/zudoku.components.js.map +1 -1
  136. package/lib/zudoku.openapi-worker.js +1 -1
  137. package/lib/zudoku.plugin-api-keys.js +40 -38
  138. package/lib/zudoku.plugin-api-keys.js.map +1 -1
  139. package/lib/zudoku.plugin-custom-pages.js +1 -1
  140. package/lib/zudoku.plugin-markdown.js +15 -14
  141. package/lib/zudoku.plugin-markdown.js.map +1 -1
  142. package/lib/zudoku.plugin-openapi.js +2 -2
  143. package/package.json +17 -6
  144. package/src/app/entry.client.tsx +14 -0
  145. package/src/lib/authentication/AuthenticationPlugin.tsx +4 -1
  146. package/src/lib/authentication/authentication.ts +1 -1
  147. package/src/lib/authentication/hook.ts +1 -3
  148. package/src/lib/authentication/providers/auth0.tsx +3 -2
  149. package/src/lib/authentication/providers/openid.tsx +12 -30
  150. package/src/lib/authentication/state.ts +44 -10
  151. package/src/lib/components/Bootstrap.tsx +25 -18
  152. package/src/lib/components/Header.tsx +42 -9
  153. package/src/lib/components/Layout.tsx +49 -37
  154. package/src/lib/components/MobileTopNavigation.tsx +11 -18
  155. package/src/lib/components/SyntaxHighlight.tsx +3 -2
  156. package/src/lib/components/ThemeSwitch.tsx +6 -4
  157. package/src/lib/components/TopNavigation.tsx +25 -17
  158. package/src/lib/core/plugins.ts +8 -0
  159. package/src/lib/oas/parser/upgrade/index.ts +4 -27
  160. package/src/lib/plugins/api-keys/index.tsx +3 -0
  161. package/src/lib/plugins/markdown/MdxPage.tsx +25 -1
  162. package/src/lib/plugins/markdown/index.tsx +1 -0
  163. package/src/lib/plugins/openapi/ColorizedParam.tsx +2 -2
  164. package/src/lib/plugins/openapi/client/GraphQLClient.tsx +17 -0
  165. package/src/lib/plugins/openapi/client/useCreateQuery.ts +5 -2
  166. package/src/lib/plugins/openapi/interfaces.ts +1 -1
  167. package/src/lib/plugins/openapi/post-processors/removeExtensions.test.ts +144 -0
  168. package/src/lib/plugins/openapi/post-processors/removeExtensions.ts +24 -0
  169. package/src/lib/plugins/openapi/post-processors/removePaths.test.ts +126 -0
  170. package/src/lib/plugins/openapi/post-processors/removePaths.ts +55 -0
  171. package/src/lib/plugins/openapi/post-processors/traverse.ts +1 -0
  172. package/src/lib/plugins/openapi/schema/SchemaView.tsx +1 -1
  173. package/src/lib/util/traverse.ts +25 -0
  174. package/lib/AuthenticationPlugin-DeGDVa1r.js +0 -56
  175. package/lib/AuthenticationPlugin-DeGDVa1r.js.map +0 -1
  176. package/lib/MdxPage-DFlbtJWi.js +0 -174
  177. package/lib/MdxPage-DFlbtJWi.js.map +0 -1
  178. package/lib/OperationList-KshJrrLL.js.map +0 -1
  179. package/lib/assets/worker-CPsGZsve.js.map +0 -1
  180. package/lib/createServer-DK-g7kbB.js.map +0 -1
  181. package/lib/index-BcesIHH4.js.map +0 -1
  182. package/lib/state-BsPrOUAh.js +0 -252
  183. package/lib/state-BsPrOUAh.js.map +0 -1
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { type RecordAny, traverse } from "../../../util/traverse.js";
2
3
  import type { OpenAPIDocument } from "../index.js";
3
4
  /**
4
5
  * Upgrade from OpenAPI 3.0.x to 3.1.0
@@ -6,30 +7,8 @@ import type { OpenAPIDocument } from "../index.js";
6
7
  * Taken from https://github.com/scalar/openapi-parser/blob/main/packages/openapi-parser/src/utils/upgradeFromThreeToThreeOne.ts
7
8
  * https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0
8
9
  */
9
- export function traverse(
10
- specification: Record<string, any>,
11
- transform: (specification: Record<string, any>) => Record<string, any>,
12
- ) {
13
- const result: Record<string, any> = {};
14
10
 
15
- for (const [key, value] of Object.entries(specification)) {
16
- if (Array.isArray(value)) {
17
- result[key] = value.map((item) =>
18
- typeof item === "object" && item !== null
19
- ? traverse(item, transform)
20
- : item,
21
- );
22
- } else if (typeof value === "object" && value !== null) {
23
- result[key] = traverse(value, transform);
24
- } else {
25
- result[key] = value;
26
- }
27
- }
28
-
29
- return transform(result);
30
- }
31
-
32
- export const upgradeSchema = (schema: Record<string, any>): OpenAPIDocument => {
11
+ export const upgradeSchema = (schema: RecordAny): OpenAPIDocument => {
33
12
  if (schema.openapi?.startsWith("3.0")) {
34
13
  schema.openapi = "3.1.0";
35
14
  }
@@ -64,9 +43,7 @@ export const upgradeSchema = (schema: Record<string, any>): OpenAPIDocument => {
64
43
  schema = traverse(schema, (sub) => {
65
44
  if (sub.example !== undefined) {
66
45
  sub.examples = {
67
- default: {
68
- value: sub.example,
69
- },
46
+ default: sub.example,
70
47
  };
71
48
  delete sub.example;
72
49
  }
@@ -77,7 +54,7 @@ export const upgradeSchema = (schema: Record<string, any>): OpenAPIDocument => {
77
54
  schema = traverse(schema, (sub) => {
78
55
  if (sub.type === "object" && sub.properties !== undefined) {
79
56
  for (const [, value] of Object.entries(sub.properties)) {
80
- const v = (value ?? {}) as Record<string, any>;
57
+ const v = (value ?? {}) as RecordAny;
81
58
  if (v.type === "string" && v.format === "binary") {
82
59
  v.contentEncoding = "application/octet-stream";
83
60
  delete v.format;
@@ -1,3 +1,4 @@
1
+ import { FileKey2Icon } from "lucide-react";
1
2
  import { type RouteObject } from "react-router-dom";
2
3
  import { ZudokuContext } from "../../core/ZudokuContext.js";
3
4
  import {
@@ -105,6 +106,8 @@ export const apiKeyPlugin = (
105
106
  {
106
107
  label: "API Keys",
107
108
  path: "/settings/api-keys",
109
+ category: "middle",
110
+ icon: FileKey2Icon,
108
111
  },
109
112
  ],
110
113
  getIdentities: async (context) => {
@@ -1,7 +1,7 @@
1
1
  import { useMDXComponents } from "@mdx-js/react";
2
2
  import slugify from "@sindresorhus/slugify";
3
3
  import { Helmet } from "@zudoku/react-helmet-async";
4
- import { type PropsWithChildren } from "react";
4
+ import { type PropsWithChildren, useEffect } from "react";
5
5
  import { Link } from "react-router-dom";
6
6
  import { CategoryHeading } from "../../components/CategoryHeading.js";
7
7
  import { Heading } from "../../components/Heading.js";
@@ -15,6 +15,14 @@ import { cn } from "../../util/cn.js";
15
15
  import { Toc } from "./Toc.js";
16
16
  import { MarkdownPluginDefaultOptions, MDXImport } from "./index.js";
17
17
 
18
+ declare global {
19
+ interface Window {
20
+ __getReactRefreshIgnoredExports?: (args: {
21
+ id: string;
22
+ }) => string[] | undefined;
23
+ }
24
+ }
25
+
18
26
  const MarkdownHeadings = {
19
27
  h2: ({ children, id }) => (
20
28
  <Heading level={2} id={id} registerSidebarAnchor>
@@ -30,11 +38,13 @@ const MarkdownHeadings = {
30
38
 
31
39
  export const MdxPage = ({
32
40
  mdxComponent: MdxComponent,
41
+ file,
33
42
  frontmatter = {},
34
43
  defaultOptions,
35
44
  tableOfContents,
36
45
  }: PropsWithChildren<
37
46
  Omit<MDXImport, "default"> & {
47
+ file: string;
38
48
  mdxComponent: MDXImport["default"];
39
49
  defaultOptions?: MarkdownPluginDefaultOptions;
40
50
  }
@@ -57,6 +67,20 @@ export const MdxPage = ({
57
67
 
58
68
  const { prev, next } = usePrevNext();
59
69
 
70
+ useEffect(() => {
71
+ if (process.env.NODE_ENV === "development") {
72
+ window.__getReactRefreshIgnoredExports = ({ id }) => {
73
+ if (!id.endsWith(file)) return;
74
+
75
+ return ["frontmatter", "tableOfContents"];
76
+ };
77
+
78
+ return () => {
79
+ window.__getReactRefreshIgnoredExports = undefined;
80
+ };
81
+ }
82
+ }, [file]);
83
+
60
84
  return (
61
85
  <div className="xl:grid grid-cols-[--sidecar-grid-cols] gap-8 justify-between">
62
86
  <Helmet>
@@ -57,6 +57,7 @@ export const markdownPlugin = (
57
57
  return {
58
58
  element: (
59
59
  <MdxPage
60
+ file={file}
60
61
  mdxComponent={Component}
61
62
  {...props}
62
63
  defaultOptions={defaultOptions}
@@ -6,10 +6,10 @@ import { pastellize } from "../../util/pastellize.js";
6
6
  export const DATA_ATTR = "data-linked-param";
7
7
 
8
8
  export const usePastellizedColor = (name: string) => {
9
- const { theme } = useTheme();
9
+ const { resolvedTheme } = useTheme();
10
10
  return pastellize(
11
11
  name,
12
- theme === "light" ? { saturation: 85, lightness: 50 } : undefined,
12
+ resolvedTheme === "light" ? { saturation: 85, lightness: 50 } : undefined,
13
13
  );
14
14
  };
15
15
 
@@ -15,6 +15,20 @@ type GraphQLResponse<TResult> = {
15
15
  data: TResult;
16
16
  };
17
17
 
18
+ const resolveVariables = async (variables?: unknown) => {
19
+ if (!variables) return;
20
+
21
+ if (
22
+ typeof variables === "object" &&
23
+ "type" in variables &&
24
+ variables.type === "file" &&
25
+ "input" in variables &&
26
+ typeof variables.input === "function"
27
+ ) {
28
+ variables.input = await variables.input();
29
+ }
30
+ };
31
+
18
32
  const throwIfError = (response: GraphQLResponse<unknown>) => {
19
33
  if (!response.errors?.[0]) return;
20
34
 
@@ -47,6 +61,9 @@ export class GraphQLClient {
47
61
  ...[variables]: TVariables extends Record<string, never> ? [] : [TVariables]
48
62
  ) => {
49
63
  const operationName = query.match(/query (\w+)/)?.[1];
64
+
65
+ await resolveVariables(variables);
66
+
50
67
  const body = JSON.stringify({ query, variables, operationName });
51
68
 
52
69
  switch (this.#mode) {
@@ -1,4 +1,5 @@
1
- import { useContext } from "react";
1
+ import hashit from "object-hash";
2
+ import { useContext, useMemo } from "react";
2
3
  import type { TypedDocumentString } from "../graphql/graphql.js";
3
4
  import { GraphQLContext } from "./GraphQLContext.js";
4
5
 
@@ -11,8 +12,10 @@ export const useCreateQuery = <TResult, TVariables>(
11
12
  throw new Error("useGraphQL must be used within a GraphQLProvider");
12
13
  }
13
14
 
15
+ const hash = useMemo(() => hashit(variables[0] ?? {}), [variables]);
16
+
14
17
  return {
15
18
  queryFn: () => graphQLClient.fetch(query, ...variables),
16
- queryKey: [query, variables[0]],
19
+ queryKey: [query, hash],
17
20
  } as const;
18
21
  };
@@ -1,6 +1,6 @@
1
1
  type OasSource =
2
2
  | { type: "url"; input: string }
3
- | { type: "file"; input: any }
3
+ | { type: "file"; input: () => Promise<unknown> }
4
4
  | { type: "raw"; input: string };
5
5
 
6
6
  export type OasPluginConfig = {
@@ -0,0 +1,144 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { removeExtensions } from "./removeExtensions.js";
3
+
4
+ const baseDoc = {
5
+ openapi: "3.1.0",
6
+ "x-root-ext": "remove me",
7
+ info: {
8
+ title: "Test API",
9
+ version: "1.0.0",
10
+ "x-info-ext": "remove me",
11
+ },
12
+ paths: {
13
+ "/test": {
14
+ "x-path-ext": "remove me",
15
+ parameters: [
16
+ {
17
+ name: "param1",
18
+ in: "query",
19
+ schema: { type: "string" },
20
+ "x-param-ext": "remove me",
21
+ },
22
+ ],
23
+ get: {
24
+ "x-operation-ext": "remove me",
25
+ responses: {
26
+ "200": {
27
+ description: "OK",
28
+ "x-response-ext": "remove me",
29
+ },
30
+ },
31
+ parameters: [
32
+ {
33
+ name: "opParam1",
34
+ in: "header",
35
+ schema: { type: "string" },
36
+ "x-op-param-ext": "remove me",
37
+ },
38
+ ],
39
+ },
40
+ },
41
+ },
42
+ tags: [
43
+ {
44
+ name: "example",
45
+ "x-tag-ext": "remove me",
46
+ },
47
+ ],
48
+ components: {
49
+ securitySchemes: {
50
+ ApiKeyAuth: {
51
+ type: "apiKey",
52
+ name: "api_key",
53
+ in: "header",
54
+ "x-security-ext": "remove me",
55
+ },
56
+ },
57
+ },
58
+ };
59
+
60
+ describe("removeExtensions", () => {
61
+ it("removes all x- extensions by default", () => {
62
+ const processed = removeExtensions()(baseDoc);
63
+
64
+ const removedExtensions = [
65
+ "x-root-ext",
66
+ "info.x-info-ext",
67
+ "paths./test.x-path-ext",
68
+ "paths./test.parameters[0].x-param-ext",
69
+ "paths./test.get.x-operation-ext",
70
+ "paths./test.get.responses.200.x-response-ext",
71
+ "paths./test.get.parameters[0].x-op-param-ext",
72
+ "tags[0].x-tag-ext",
73
+ "components.securitySchemes.ApiKeyAuth.x-security-ext",
74
+ ];
75
+
76
+ removedExtensions.forEach((ext) => {
77
+ expect(processed).not.toHaveProperty(ext.split("."));
78
+ });
79
+
80
+ // Assert that non-x- fields remain unchanged
81
+ expect(processed.openapi).toBe("3.1.0");
82
+ expect(processed.info.title).toBe("Test API");
83
+ expect(processed).toHaveProperty(
84
+ ["paths", "/test", "get", "responses", "200", "description"],
85
+ "OK",
86
+ );
87
+ expect(processed.tags[0].name).toBe("example");
88
+ });
89
+
90
+ it("removes only specified x- extensions when names are provided", () => {
91
+ const docWithExtraExtensions = {
92
+ ...baseDoc,
93
+ info: { ...baseDoc.info, "x-other-ext": "keep me" },
94
+ };
95
+
96
+ const processed = removeExtensions({
97
+ keys: ["x-path-ext", "x-param-ext"],
98
+ })(docWithExtraExtensions);
99
+
100
+ // Assert specified extensions are removed
101
+ expect(processed.paths["/test"]["x-path-ext"]).toBeUndefined();
102
+ expect(
103
+ processed.paths["/test"].parameters[0]["x-param-ext"],
104
+ ).toBeUndefined();
105
+
106
+ // Assert other x- fields remain
107
+ expect(processed["x-root-ext"]).toBe("remove me");
108
+ expect(processed.info["x-info-ext"]).toBe("remove me");
109
+ expect(processed.info["x-other-ext"]).toBe("keep me");
110
+ });
111
+
112
+ it("handles deeply nested extensions", () => {
113
+ const deeplyNested = {
114
+ a: {
115
+ b: {
116
+ c: {
117
+ "x-deep-ext": "remove me",
118
+ d: {
119
+ e: "value",
120
+ "x-another-ext": "remove me",
121
+ },
122
+ },
123
+ },
124
+ },
125
+ };
126
+
127
+ const processed = removeExtensions()(deeplyNested);
128
+
129
+ expect(processed.a.b.c["x-deep-ext"]).toBeUndefined();
130
+ expect(processed.a.b.c.d["x-another-ext"]).toBeUndefined();
131
+ expect(processed.a.b.c.d.e).toBe("value");
132
+ });
133
+
134
+ it("does nothing if no x- extensions are present", () => {
135
+ const docWithoutExtensions = {
136
+ openapi: "3.1.0",
137
+ info: { title: "API without extensions" },
138
+ };
139
+
140
+ const processed = removeExtensions()(docWithoutExtensions);
141
+
142
+ expect(processed).toEqual(docWithoutExtensions);
143
+ });
144
+ });
@@ -0,0 +1,24 @@
1
+ import { type RecordAny, traverse } from "./traverse.js";
2
+
3
+ interface RemoveExtensionsOptions {
4
+ keys?: string[];
5
+ }
6
+
7
+ // Remove all `x-` prefixed key/value pairs, or filter by names if provided
8
+ export const removeExtensions =
9
+ ({ keys }: RemoveExtensionsOptions = {}) =>
10
+ (doc: RecordAny): RecordAny =>
11
+ traverse(doc, (spec) => {
12
+ const result: RecordAny = {};
13
+
14
+ for (const [key, value] of Object.entries(spec)) {
15
+ const isExtension = key.startsWith("x-");
16
+ const shouldRemove =
17
+ isExtension && (keys === undefined || keys.includes(key));
18
+
19
+ if (shouldRemove) continue;
20
+
21
+ result[key] = value;
22
+ }
23
+ return result;
24
+ });
@@ -0,0 +1,126 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { removePaths } from "./removePaths.js";
3
+
4
+ const baseDoc = {
5
+ openapi: "3.0.3",
6
+ paths: {
7
+ "/example": {
8
+ get: { summary: "Get example" },
9
+ post: { summary: "Post example" },
10
+ },
11
+ "/remove-me": {
12
+ delete: { summary: "Delete example" },
13
+ },
14
+ "/another": {
15
+ get: { summary: "Another example" },
16
+ },
17
+ },
18
+ };
19
+
20
+ describe("removePaths", () => {
21
+ it("removes paths specified in the paths option", () => {
22
+ const processed = removePaths({
23
+ paths: {
24
+ "/remove-me": true,
25
+ },
26
+ })(baseDoc);
27
+
28
+ expect(processed.paths["/remove-me"]).toBeUndefined();
29
+ expect(processed.paths["/example"]).toBeDefined();
30
+ expect(processed.paths["/another"]).toBeDefined();
31
+ });
32
+
33
+ it("removes specific methods in the paths option", () => {
34
+ const processed = removePaths({
35
+ paths: {
36
+ "/example": ["get"],
37
+ },
38
+ })(baseDoc);
39
+
40
+ expect(processed.paths["/example"].get).toBeUndefined();
41
+ expect(processed.paths["/example"].post).toBeDefined();
42
+ expect(processed.paths["/remove-me"]).toBeDefined();
43
+ });
44
+
45
+ it("removes paths and methods using paths and shouldRemove together", () => {
46
+ const processed = removePaths({
47
+ paths: {
48
+ "/example": ["post"],
49
+ },
50
+ shouldRemove: ({ path }) => path.startsWith("/remove"),
51
+ })(baseDoc);
52
+
53
+ expect(processed.paths["/remove-me"]).toBeUndefined();
54
+ expect(processed.paths["/example"].get).toBeDefined();
55
+ expect(processed.paths["/example"].post).toBeUndefined();
56
+ expect(processed.paths["/another"]).toBeDefined();
57
+ });
58
+
59
+ it("removes paths based on shouldRemove callback", () => {
60
+ const processed = removePaths({
61
+ shouldRemove: ({ path }) => path.startsWith("/remove"),
62
+ })(baseDoc);
63
+
64
+ expect(processed.paths["/remove-me"]).toBeUndefined();
65
+ expect(processed.paths["/example"]).toBeDefined();
66
+ expect(processed.paths["/another"]).toBeDefined();
67
+ });
68
+
69
+ it("removes methods based on shouldRemove callback", () => {
70
+ const processed = removePaths({
71
+ shouldRemove: ({ method }) => method === "post",
72
+ })(baseDoc);
73
+
74
+ expect(processed.paths["/example"].post).toBeUndefined();
75
+ expect(processed.paths["/example"].get).toBeDefined();
76
+ expect(processed.paths["/remove-me"]).toBeDefined();
77
+ });
78
+
79
+ it("removes both paths and methods based on shouldRemove callback", () => {
80
+ const processed = removePaths({
81
+ shouldRemove: ({ path, method }) =>
82
+ path.startsWith("/remove") || method === "post",
83
+ })(baseDoc);
84
+
85
+ expect(processed.paths["/remove-me"]).toBeUndefined();
86
+ expect(processed.paths["/example"].post).toBeUndefined();
87
+ expect(processed.paths["/example"].get).toBeDefined();
88
+ expect(processed.paths["/another"]).toBeDefined();
89
+ });
90
+
91
+ it("does nothing if shouldRemove always returns false", () => {
92
+ const processed = removePaths({
93
+ shouldRemove: () => false,
94
+ })(baseDoc);
95
+
96
+ expect(processed).toEqual(baseDoc);
97
+ });
98
+
99
+ it("removes everything if shouldRemove always returns true", () => {
100
+ const processed = removePaths({
101
+ shouldRemove: () => true,
102
+ })(baseDoc);
103
+
104
+ expect(processed.paths).toEqual({});
105
+ });
106
+
107
+ it("removes entire paths via shouldRemove callback", () => {
108
+ const processed = removePaths({
109
+ shouldRemove: ({ path, method }) =>
110
+ method === true && path === "/remove-me",
111
+ })(baseDoc);
112
+
113
+ expect(processed.paths["/remove-me"]).toBeUndefined();
114
+ expect(processed.paths["/example"]).toBeDefined();
115
+ expect(processed.paths["/another"]).toBeDefined();
116
+ });
117
+
118
+ it("removes specific methods while keeping paths", () => {
119
+ const processed = removePaths({
120
+ shouldRemove: ({ method }) => method === "delete",
121
+ })(baseDoc);
122
+
123
+ expect(processed.paths["/remove-me"]).toBeDefined();
124
+ expect(processed.paths["/remove-me"].delete).toBeUndefined();
125
+ });
126
+ });
@@ -0,0 +1,55 @@
1
+ import { type RecordAny, traverse } from "./traverse.js";
2
+
3
+ interface RemovePathsOptions {
4
+ // Path definitions, e.g., { '/path': true, '/path-2': ['get'] }
5
+ paths?: Record<string, true | string[]>;
6
+ shouldRemove?: (options: {
7
+ path: string;
8
+ method: true | string;
9
+ operation: RecordAny;
10
+ }) => boolean;
11
+ }
12
+
13
+ export const removePaths =
14
+ ({ paths = {}, shouldRemove }: RemovePathsOptions) =>
15
+ (doc: RecordAny): RecordAny =>
16
+ traverse(doc, (spec) => {
17
+ if (!spec.paths) return spec;
18
+
19
+ const updatedPaths: RecordAny = {};
20
+
21
+ for (const [path, methods] of Object.entries(spec.paths)) {
22
+ const operations = spec.paths[path];
23
+
24
+ // If the path is explicitly marked for removal in `paths`
25
+ if (paths[path] === true) continue;
26
+
27
+ // If the path should be removed via `shouldRemove`
28
+ if (shouldRemove?.({ path, method: true, operation: operations }))
29
+ continue;
30
+
31
+ if (typeof methods === "object" && methods !== null) {
32
+ const filteredPath = Object.fromEntries(
33
+ Object.entries(methods).filter(([method]) => {
34
+ const operations = spec.paths[path][method];
35
+ const isMethodToRemove =
36
+ Array.isArray(paths[path]) && paths[path].includes(method);
37
+
38
+ const isMethodFiltered = shouldRemove?.({
39
+ path,
40
+ method,
41
+ operation: operations,
42
+ });
43
+
44
+ return !isMethodToRemove && !isMethodFiltered;
45
+ }),
46
+ );
47
+
48
+ updatedPaths[path] = filteredPath;
49
+ } else {
50
+ updatedPaths[path] = methods;
51
+ }
52
+ }
53
+
54
+ return { ...spec, paths: updatedPaths };
55
+ });
@@ -0,0 +1 @@
1
+ export { traverse, type RecordAny } from "../../../util/traverse.js";
@@ -73,7 +73,7 @@ export const SchemaView = ({
73
73
  ) {
74
74
  return (
75
75
  <Card className="p-4 flex gap-2 items-center">
76
- {"name" in schema && <>{schema.name}</>}
76
+ {"name" in schema && <>{schema.name as string}</>}
77
77
  <span className="text-sm text-muted-foreground">object</span>
78
78
  {schema.description && (
79
79
  <Markdown
@@ -0,0 +1,25 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
+ export type RecordAny = Record<string, any>;
3
+
4
+ export const traverse = (
5
+ specification: RecordAny,
6
+ transform: (specification: RecordAny) => RecordAny,
7
+ ) => {
8
+ const result: RecordAny = {};
9
+
10
+ for (const [key, value] of Object.entries(specification)) {
11
+ if (Array.isArray(value)) {
12
+ result[key] = value.map((item) =>
13
+ typeof item === "object" && item !== null
14
+ ? traverse(item, transform)
15
+ : item,
16
+ );
17
+ } else if (typeof value === "object" && value !== null) {
18
+ result[key] = traverse(value, transform);
19
+ } else {
20
+ result[key] = value;
21
+ }
22
+ }
23
+
24
+ return transform(result);
25
+ };
@@ -1,56 +0,0 @@
1
- import { j as i } from "./jsx-runtime-B6kdoens.js";
2
- import { useEffect as o } from "react";
3
- import { u as a } from "./index-Yn8c3UWE.js";
4
- import { u as s } from "./utils-DcpDOncX.js";
5
- import { a as u } from "./index-Czzd9rjU.js";
6
- const r = () => {
7
- const t = s(), [n] = a();
8
- return o(() => {
9
- var e;
10
- (e = t.authentication) == null || e.signIn({
11
- redirectTo: n.get("redirect") ?? void 0
12
- });
13
- }, [t.authentication, n]), null;
14
- }, c = () => {
15
- const t = s(), n = u();
16
- return o(() => {
17
- var e;
18
- (e = t.authentication) == null || e.signOut().then(() => n("/"));
19
- }, [n, t.authentication]), null;
20
- }, g = () => {
21
- const t = s();
22
- return o(() => {
23
- var n, e;
24
- ((n = t.authentication) == null ? void 0 : n.signUp()) ?? ((e = t.authentication) == null || e.signIn());
25
- }, [t.authentication]), null;
26
- };
27
- class f {
28
- getRoutes() {
29
- return [
30
- {
31
- path: "/signout",
32
- element: /* @__PURE__ */ i.jsx(c, {})
33
- },
34
- {
35
- path: "/signin",
36
- element: /* @__PURE__ */ i.jsx(r, {})
37
- },
38
- {
39
- path: "/signup",
40
- element: /* @__PURE__ */ i.jsx(g, {})
41
- }
42
- ];
43
- }
44
- getProfileMenuItems() {
45
- return [
46
- {
47
- label: "Logout",
48
- path: "/signout"
49
- }
50
- ];
51
- }
52
- }
53
- export {
54
- f as A
55
- };
56
- //# sourceMappingURL=AuthenticationPlugin-DeGDVa1r.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AuthenticationPlugin-DeGDVa1r.js","sources":["../src/lib/authentication/components/SignIn.tsx","../src/lib/authentication/components/SignOut.tsx","../src/lib/authentication/components/SignUp.tsx","../src/lib/authentication/AuthenticationPlugin.tsx"],"sourcesContent":["import { useEffect } from \"react\";\nimport { useSearchParams } from \"react-router-dom\";\nimport { useZudoku } from \"../../components/context/ZudokuContext.js\";\n\nexport const SignIn = () => {\n const context = useZudoku();\n const [search] = useSearchParams();\n useEffect(() => {\n void context.authentication?.signIn({\n redirectTo: search.get(\"redirect\") ?? undefined,\n });\n }, [context.authentication, search]);\n\n return null;\n};\n","import { useEffect } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useZudoku } from \"../../components/context/ZudokuContext.js\";\n\nexport const SignOut = () => {\n const context = useZudoku();\n const navigate = useNavigate();\n useEffect(() => {\n void context.authentication?.signOut().then(() => navigate(\"/\"));\n }, [navigate, context.authentication]);\n\n return null;\n};\n","import { useEffect } from \"react\";\nimport { useZudoku } from \"../../components/context/ZudokuContext.js\";\n\nexport const SignUp = () => {\n const context = useZudoku();\n useEffect(() => {\n void (context.authentication?.signUp() ?? context.authentication?.signIn());\n }, [context.authentication]);\n\n return null;\n};\n","import {\n CommonPlugin,\n NavigationPlugin,\n ProfileMenuPlugin,\n} from \"../core/plugins.js\";\nimport { SignIn } from \"./components/SignIn.js\";\nimport { SignOut } from \"./components/SignOut.js\";\nimport { SignUp } from \"./components/SignUp.js\";\n\ntype PluginInterface = NavigationPlugin & CommonPlugin & ProfileMenuPlugin;\n\nexport class AuthenticationPlugin implements PluginInterface {\n getRoutes() {\n return [\n {\n path: \"/signout\",\n element: <SignOut />,\n },\n {\n path: \"/signin\",\n element: <SignIn />,\n },\n {\n path: \"/signup\",\n element: <SignUp />,\n },\n ];\n }\n\n getProfileMenuItems() {\n return [\n {\n label: \"Logout\",\n path: \"/signout\",\n },\n ];\n }\n}\n"],"names":["SignIn","context","useZudoku","search","useSearchParams","useEffect","_a","SignOut","navigate","useNavigate","SignUp","_b","AuthenticationPlugin"],"mappings":";;;;;AAIO,MAAMA,IAAS,MAAM;AAC1B,QAAMC,IAAUC,KACV,CAACC,CAAM,IAAIC;AACjB,SAAAC,EAAU,MAAM;;AACT,KAAAC,IAAAL,EAAQ,mBAAR,QAAAK,EAAwB,OAAO;AAAA,MAClC,YAAYH,EAAO,IAAI,UAAU,KAAK;AAAA,IAAA;AAAA,EAEvC,GAAA,CAACF,EAAQ,gBAAgBE,CAAM,CAAC,GAE5B;AACT,GCVaI,IAAU,MAAM;AAC3B,QAAMN,IAAUC,KACVM,IAAWC;AACjB,SAAAJ,EAAU,MAAM;;AACT,KAAAC,IAAAL,EAAQ,mBAAR,QAAAK,EAAwB,UAAU,KAAK,MAAME,EAAS,GAAG;AAAA,EAC7D,GAAA,CAACA,GAAUP,EAAQ,cAAc,CAAC,GAE9B;AACT,GCTaS,IAAS,MAAM;AAC1B,QAAMT,IAAUC;AAChB,SAAAG,EAAU,MAAM;;AACd,MAAMC,IAAAL,EAAQ,mBAAR,gBAAAK,EAAwB,eAAYK,IAAAV,EAAQ,mBAAR,QAAAU,EAAwB;AAAA,EAAO,GACxE,CAACV,EAAQ,cAAc,CAAC,GAEpB;AACT;ACCO,MAAMW,EAAgD;AAAA,EAC3D,YAAY;AACH,WAAA;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,+BAAUL,GAAQ,EAAA;AAAA,MACpB;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,+BAAUP,GAAO,EAAA;AAAA,MACnB;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,+BAAUU,GAAO,EAAA;AAAA,MACnB;AAAA,IAAA;AAAA,EAEJ;AAAA,EAEA,sBAAsB;AACb,WAAA;AAAA,MACL;AAAA,QACE,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACF;"}