zudoku 0.35.5 → 0.36.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 (131) hide show
  1. package/dist/app/entry.server.js +5 -1
  2. package/dist/app/entry.server.js.map +1 -1
  3. package/dist/config/validators/common.d.ts +176 -176
  4. package/dist/config/validators/common.js +8 -6
  5. package/dist/config/validators/common.js.map +1 -1
  6. package/dist/config/validators/validate.d.ts +63 -63
  7. package/dist/lib/authentication/providers/openid.js +11 -7
  8. package/dist/lib/authentication/providers/openid.js.map +1 -1
  9. package/dist/lib/components/AnchorLink.js +3 -2
  10. package/dist/lib/components/AnchorLink.js.map +1 -1
  11. package/dist/lib/components/Layout.js +3 -14
  12. package/dist/lib/components/Layout.js.map +1 -1
  13. package/dist/lib/components/Zudoku.js +3 -1
  14. package/dist/lib/components/Zudoku.js.map +1 -1
  15. package/dist/lib/components/cache.d.ts +7 -0
  16. package/dist/lib/components/cache.js +7 -0
  17. package/dist/lib/components/cache.js.map +1 -1
  18. package/dist/lib/components/context/ViewportAnchorContext.js +3 -6
  19. package/dist/lib/components/context/ViewportAnchorContext.js.map +1 -1
  20. package/dist/lib/components/context/ZudokuContext.d.ts +1 -1
  21. package/dist/lib/components/context/ZudokuContext.js +4 -3
  22. package/dist/lib/components/context/ZudokuContext.js.map +1 -1
  23. package/dist/lib/components/navigation/SidebarItem.js +3 -2
  24. package/dist/lib/components/navigation/SidebarItem.js.map +1 -1
  25. package/dist/lib/core/ZudokuContext.d.ts +8 -6
  26. package/dist/lib/core/ZudokuContext.js +4 -2
  27. package/dist/lib/core/ZudokuContext.js.map +1 -1
  28. package/dist/lib/core/plugins.d.ts +3 -3
  29. package/dist/lib/hooks/useEvent.test.js +1 -1
  30. package/dist/lib/hooks/useEvent.test.js.map +1 -1
  31. package/dist/lib/oas/graphql/index.d.ts +16 -4
  32. package/dist/lib/oas/graphql/index.js +59 -43
  33. package/dist/lib/oas/graphql/index.js.map +1 -1
  34. package/dist/lib/plugins/openapi/OperationList.js +19 -5
  35. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  36. package/dist/lib/plugins/openapi/OperationListItem.js +1 -1
  37. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  38. package/dist/lib/plugins/openapi/Sidecar.js +2 -2
  39. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  40. package/dist/lib/plugins/openapi/client/useCreateQuery.d.ts +1 -1
  41. package/dist/lib/plugins/openapi/client/useCreateQuery.js +2 -1
  42. package/dist/lib/plugins/openapi/client/useCreateQuery.js.map +1 -1
  43. package/dist/lib/plugins/openapi/graphql/gql.d.ts +4 -4
  44. package/dist/lib/plugins/openapi/graphql/gql.js +3 -3
  45. package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
  46. package/dist/lib/plugins/openapi/graphql/graphql.d.ts +34 -45
  47. package/dist/lib/plugins/openapi/graphql/graphql.js +20 -30
  48. package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
  49. package/dist/lib/plugins/openapi/index.d.ts +5 -10
  50. package/dist/lib/plugins/openapi/index.js +29 -60
  51. package/dist/lib/plugins/openapi/index.js.map +1 -1
  52. package/dist/lib/plugins/openapi/interfaces.d.ts +3 -1
  53. package/dist/lib/plugins/openapi/util/createSidebarCategory.js +5 -7
  54. package/dist/lib/plugins/openapi/util/createSidebarCategory.js.map +1 -1
  55. package/dist/lib/util/traverse.js +2 -2
  56. package/dist/lib/util/traverse.js.map +1 -1
  57. package/dist/lib/util/useScrollToAnchor.js +2 -0
  58. package/dist/lib/util/useScrollToAnchor.js.map +1 -1
  59. package/dist/vite/api/schema-codegen.js +19 -4
  60. package/dist/vite/api/schema-codegen.js.map +1 -1
  61. package/dist/vite/api/schema-codegen.test.js +61 -0
  62. package/dist/vite/api/schema-codegen.test.js.map +1 -1
  63. package/dist/vite/plugin-api.js +3 -11
  64. package/dist/vite/plugin-api.js.map +1 -1
  65. package/lib/{AuthenticationPlugin-4ip08maU.js → AuthenticationPlugin-Cr6xjOJD.js} +2 -2
  66. package/lib/{AuthenticationPlugin-4ip08maU.js.map → AuthenticationPlugin-Cr6xjOJD.js.map} +1 -1
  67. package/lib/{Markdown-hBN9vkm5.js → Markdown-BlioIqkZ.js} +313 -311
  68. package/lib/Markdown-BlioIqkZ.js.map +1 -0
  69. package/lib/{MdxPage-UCWwxhzC.js → MdxPage-7XnN9J9R.js} +3 -3
  70. package/lib/{MdxPage-UCWwxhzC.js.map → MdxPage-7XnN9J9R.js.map} +1 -1
  71. package/lib/{OasProvider-CJ8KOnsH.js → OasProvider-BaRRMSsD.js} +3 -3
  72. package/lib/{OasProvider-CJ8KOnsH.js.map → OasProvider-BaRRMSsD.js.map} +1 -1
  73. package/lib/{OperationList-C4rpJdcE.js → OperationList-BjL1hzSx.js} +988 -961
  74. package/lib/OperationList-BjL1hzSx.js.map +1 -0
  75. package/lib/{SlotletProvider-D-XPr1Wg.js → SlotletProvider-CXb3wQiR.js} +2 -2
  76. package/lib/{SlotletProvider-D-XPr1Wg.js.map → SlotletProvider-CXb3wQiR.js.map} +1 -1
  77. package/lib/{circular-v7K6lDDh.js → circular-ByJI6Mci.js} +4887 -4419
  78. package/lib/circular-ByJI6Mci.js.map +1 -0
  79. package/lib/{createServer-BEFAOb-x.js → createServer-DjgKDpGV.js} +3887 -4291
  80. package/lib/createServer-DjgKDpGV.js.map +1 -0
  81. package/lib/{hook-CfCFKZ-2.js → hook-Bo80UX00.js} +75 -74
  82. package/lib/hook-Bo80UX00.js.map +1 -0
  83. package/lib/{index-Dowg8c_k.js → index-D5m8_oyY.js} +612 -650
  84. package/lib/index-D5m8_oyY.js.map +1 -0
  85. package/lib/post-processors/traverse.js +2 -2
  86. package/lib/post-processors/traverse.js.map +1 -1
  87. package/lib/zudoku.auth-auth0.js +1 -1
  88. package/lib/zudoku.auth-clerk.js +2 -2
  89. package/lib/zudoku.auth-openid.js +283 -282
  90. package/lib/zudoku.auth-openid.js.map +1 -1
  91. package/lib/zudoku.components.js +395 -397
  92. package/lib/zudoku.components.js.map +1 -1
  93. package/lib/zudoku.hooks.js +1 -1
  94. package/lib/zudoku.plugin-api-catalog.js +2 -2
  95. package/lib/zudoku.plugin-api-keys.js +2 -2
  96. package/lib/zudoku.plugin-custom-pages.js +1 -1
  97. package/lib/zudoku.plugin-markdown.js +1 -1
  98. package/lib/zudoku.plugin-openapi.js +4 -5
  99. package/lib/zudoku.plugin-openapi.js.map +1 -1
  100. package/lib/zudoku.plugins.js.map +1 -1
  101. package/package.json +5 -5
  102. package/src/app/entry.server.tsx +7 -1
  103. package/src/lib/authentication/providers/openid.tsx +12 -9
  104. package/src/lib/components/AnchorLink.tsx +4 -2
  105. package/src/lib/components/Layout.tsx +3 -16
  106. package/src/lib/components/Zudoku.tsx +5 -1
  107. package/src/lib/components/cache.ts +8 -0
  108. package/src/lib/components/context/ViewportAnchorContext.tsx +3 -6
  109. package/src/lib/components/context/ZudokuContext.ts +5 -4
  110. package/src/lib/components/navigation/SidebarItem.tsx +3 -2
  111. package/src/lib/core/ZudokuContext.ts +11 -8
  112. package/src/lib/core/plugins.ts +4 -4
  113. package/src/lib/hooks/useEvent.test.tsx +1 -1
  114. package/src/lib/oas/graphql/index.ts +116 -76
  115. package/src/lib/plugins/openapi/OperationList.tsx +31 -37
  116. package/src/lib/plugins/openapi/OperationListItem.tsx +1 -1
  117. package/src/lib/plugins/openapi/Sidecar.tsx +2 -2
  118. package/src/lib/plugins/openapi/client/useCreateQuery.ts +2 -1
  119. package/src/lib/plugins/openapi/graphql/gql.ts +17 -17
  120. package/src/lib/plugins/openapi/graphql/graphql.ts +62 -79
  121. package/src/lib/plugins/openapi/index.tsx +40 -84
  122. package/src/lib/plugins/openapi/interfaces.ts +4 -1
  123. package/src/lib/plugins/openapi/util/createSidebarCategory.tsx +5 -7
  124. package/src/lib/util/traverse.ts +2 -2
  125. package/src/lib/util/useScrollToAnchor.ts +2 -0
  126. package/lib/Markdown-hBN9vkm5.js.map +0 -1
  127. package/lib/OperationList-C4rpJdcE.js.map +0 -1
  128. package/lib/circular-v7K6lDDh.js.map +0 -1
  129. package/lib/createServer-BEFAOb-x.js.map +0 -1
  130. package/lib/hook-CfCFKZ-2.js.map +0 -1
  131. package/lib/index-Dowg8c_k.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import { z as f } from "./index-DwT-v3zK.js";
2
2
  import { useState as m, useEffect as i } from "react";
3
- import { a } from "./hook-CfCFKZ-2.js";
3
+ import { a } from "./hook-Bo80UX00.js";
4
4
  function d(e, t) {
5
5
  const s = a(), [n, o] = m();
6
6
  return i(() => s.addEventListener(e, (...u) => {
@@ -2,10 +2,10 @@ import { j as e } from "./jsx-runtime-CYK1ROHF.js";
2
2
  import { s as h } from "./index-LNp6rxyU.js";
3
3
  import { d as b, m as x } from "./chunk-HA7DTUK3-ZGg2W6yV.js";
4
4
  import { j as d } from "./joinUrl-10po2Jdj.js";
5
- import { u as j, b as y } from "./hook-CfCFKZ-2.js";
5
+ import { u as j, b as y } from "./hook-Bo80UX00.js";
6
6
  import { H as v } from "./index.esm-CltAN0Tf.js";
7
7
  import { Link as N } from "./zudoku.components.js";
8
- import { H as S, M as w } from "./Markdown-hBN9vkm5.js";
8
+ import { H as S, M as w } from "./Markdown-BlioIqkZ.js";
9
9
  const H = ({
10
10
  items: s,
11
11
  filterCatalogItems: l = (n) => n,
@@ -1,12 +1,12 @@
1
1
  import { j as e } from "./jsx-runtime-CYK1ROHF.js";
2
2
  import { RotateCwIcon as j, TrashIcon as v, EyeOffIcon as w, EyeIcon as K, CheckIcon as b, CopyIcon as k, FileKey2Icon as N } from "lucide-react";
3
- import { D as I, S as x, R as S } from "./SlotletProvider-D-XPr1Wg.js";
3
+ import { D as I, S as x, R as S } from "./SlotletProvider-CXb3wQiR.js";
4
4
  import { i as c } from "./invariant-Caa8-XvF.js";
5
5
  import { u as h } from "./useQuery-CQUwWR9i.js";
6
6
  import { u as d, S as A, a as C, b as E, c as P, d as D, e as p } from "./Select-FAYHOYTy.js";
7
7
  import { a as q } from "./index.esm--gIChbWs.js";
8
8
  import { a as R, L as u, O } from "./chunk-HA7DTUK3-ZGg2W6yV.js";
9
- import { a as g, e as z, b as F } from "./hook-CfCFKZ-2.js";
9
+ import { a as g, e as z, b as F } from "./hook-Bo80UX00.js";
10
10
  import { Button as l } from "./ui/Button.js";
11
11
  import { Input as T } from "./ui/Input.js";
12
12
  import { useState as y } from "react";
@@ -1,6 +1,6 @@
1
1
  import { j as o } from "./jsx-runtime-CYK1ROHF.js";
2
2
  import a from "react";
3
- import { P as n } from "./Markdown-hBN9vkm5.js";
3
+ import { P as n } from "./Markdown-BlioIqkZ.js";
4
4
  import { c } from "./cn-qaFjX9_3.js";
5
5
  import { u as p } from "./useExposedProps-BslIn-FE.js";
6
6
  const u = ({
@@ -53,7 +53,7 @@ const P = (e) => ({
53
53
  const u = {
54
54
  path: r,
55
55
  lazy: async () => {
56
- const { MdxPage: p } = await import("./MdxPage-UCWwxhzC.js"), { default: f, ...l } = await i();
56
+ const { MdxPage: p } = await import("./MdxPage-7XnN9J9R.js"), { default: f, ...l } = await i();
57
57
  return {
58
58
  element: /* @__PURE__ */ d.jsx(
59
59
  p,
@@ -1,13 +1,12 @@
1
1
  import "./jsx-runtime-CYK1ROHF.js";
2
- import "./index-LNp6rxyU.js";
3
2
  import "lucide-react";
4
3
  import "./chunk-HA7DTUK3-ZGg2W6yV.js";
5
- import "./hook-CfCFKZ-2.js";
4
+ import "./hook-Bo80UX00.js";
6
5
  import "./ui/Button.js";
7
6
  import "./joinUrl-10po2Jdj.js";
8
- import { U as n, o as s } from "./index-Dowg8c_k.js";
7
+ import { U as e, o as n } from "./index-D5m8_oyY.js";
9
8
  export {
10
- n as UNTAGGED_PATH,
11
- s as openApiPlugin
9
+ e as UNTAGGED_PATH,
10
+ n as openApiPlugin
12
11
  };
13
12
  //# sourceMappingURL=zudoku.plugin-openapi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"zudoku.plugin-openapi.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
1
+ {"version":3,"file":"zudoku.plugin-openapi.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"zudoku.plugins.js","sources":["../src/lib/core/plugins.ts"],"sourcesContent":["import type { LucideIcon } from \"lucide-react\";\nimport { type ReactElement } from \"react\";\nimport { type RouteObject } from \"react-router\";\nimport type { Sidebar } from \"../../config/validators/SidebarSchema.js\";\nimport { MdxComponentsType } from \"../util/MdxComponents.js\";\nimport {\n ZudokuContext,\n ZudokuEvents,\n type ApiIdentity,\n} from \"./ZudokuContext.js\";\n\nexport type ZudokuPlugin =\n | CommonPlugin\n | ProfileMenuPlugin\n | NavigationPlugin\n | ApiIdentityPlugin\n | SearchProviderPlugin\n | EventConsumerPlugin;\n\nexport type { RouteObject };\n\nexport interface NavigationPlugin {\n getRoutes: () => RouteObject[];\n getSidebar?: (path: string) => Promise<Sidebar>;\n}\n\nexport const createApiIdentityPlugin = (\n plugin: ApiIdentityPlugin,\n): ApiIdentityPlugin => plugin;\n\nexport const createProfileMenuPlugin = (\n plugin: ProfileMenuPlugin,\n): ProfileMenuPlugin => plugin;\n\nexport interface ApiIdentityPlugin {\n getIdentities: (context: ZudokuContext) => Promise<ApiIdentity[]>;\n}\n\nexport interface SearchProviderPlugin {\n renderSearch: (o: {\n isOpen: boolean;\n onClose: () => void;\n }) => React.JSX.Element | null;\n}\n\nexport interface ProfileMenuPlugin {\n getProfileMenuItems: (context: ZudokuContext) => ProfileNavigationItem[];\n}\n\nexport type ProfileNavigationItem = {\n label: string;\n path?: string;\n weight?: number;\n category?: \"top\" | \"middle\" | \"bottom\";\n children?: ProfileNavigationItem[];\n icon?: LucideIcon;\n};\n\nexport interface CommonPlugin {\n initialize?: (\n context: ZudokuContext,\n ) => Promise<void | boolean> | void | boolean;\n getHead?: () => ReactElement | undefined;\n getMdxComponents?: () => MdxComponentsType;\n}\n\nexport type EventConsumerPlugin<Event extends ZudokuEvents = ZudokuEvents> = {\n events: { [K in keyof Event]: Event[K] };\n};\n\nexport const isEventConsumerPlugin = (\n obj: ZudokuPlugin,\n): obj is EventConsumerPlugin =>\n \"events\" in obj && typeof obj.events === \"object\";\n\nexport const isProfileMenuPlugin = (\n obj: ZudokuPlugin,\n): obj is ProfileMenuPlugin =>\n \"getProfileMenuItems\" in obj && typeof obj.getProfileMenuItems === \"function\";\n\nexport const isNavigationPlugin = (\n obj: ZudokuPlugin,\n): obj is NavigationPlugin =>\n \"getRoutes\" in obj && typeof obj.getRoutes === \"function\";\n\nexport const isSearchPlugin = (\n obj: ZudokuPlugin,\n): obj is SearchProviderPlugin =>\n \"renderSearch\" in obj && typeof obj.renderSearch === \"function\";\n\nexport const needsInitialization = (obj: ZudokuPlugin): obj is CommonPlugin =>\n \"initialize\" in obj && typeof obj.initialize === \"function\";\n\nexport const hasHead = (obj: ZudokuPlugin): obj is CommonPlugin =>\n \"getHead\" in obj && typeof obj.getHead === \"function\";\n\nexport const isMdxProviderPlugin = (obj: ZudokuPlugin): obj is CommonPlugin =>\n \"getMdxComponents\" in obj && typeof obj.getMdxComponents === \"function\";\n\nexport const isApiIdentityPlugin = (\n obj: ZudokuPlugin,\n): obj is ApiIdentityPlugin =>\n \"getIdentities\" in obj && typeof obj.getIdentities === \"function\";\n"],"names":["createApiIdentityPlugin","plugin","createProfileMenuPlugin","isEventConsumerPlugin","obj","isProfileMenuPlugin","isNavigationPlugin","isSearchPlugin","needsInitialization","hasHead","isMdxProviderPlugin","isApiIdentityPlugin"],"mappings":"AA0Ba,MAAAA,IAA0B,CACrCC,MACsBA,GAEXC,IAA0B,CACrCD,MACsBA,GAsCXE,IAAwB,CACnCC,MAEA,YAAYA,KAAO,OAAOA,EAAI,UAAW,UAE9BC,IAAsB,CACjCD,MAEA,yBAAyBA,KAAO,OAAOA,EAAI,uBAAwB,YAExDE,IAAqB,CAChCF,MAEA,eAAeA,KAAO,OAAOA,EAAI,aAAc,YAEpCG,IAAiB,CAC5BH,MAEA,kBAAkBA,KAAO,OAAOA,EAAI,gBAAiB,YAE1CI,IAAsB,CAACJ,MAClC,gBAAgBA,KAAO,OAAOA,EAAI,cAAe,YAEtCK,IAAU,CAACL,MACtB,aAAaA,KAAO,OAAOA,EAAI,WAAY,YAEhCM,IAAsB,CAACN,MAClC,sBAAsBA,KAAO,OAAOA,EAAI,oBAAqB,YAElDO,IAAsB,CACjCP,MAEA,mBAAmBA,KAAO,OAAOA,EAAI,iBAAkB;"}
1
+ {"version":3,"file":"zudoku.plugins.js","sources":["../src/lib/core/plugins.ts"],"sourcesContent":["import type { LucideIcon } from \"lucide-react\";\nimport { type ReactElement } from \"react\";\nimport { type RouteObject } from \"react-router\";\nimport type { Sidebar } from \"../../config/validators/SidebarSchema.js\";\nimport { type MdxComponentsType } from \"../util/MdxComponents.js\";\nimport {\n type ApiIdentity,\n type ZudokuContext,\n type ZudokuEvents,\n} from \"./ZudokuContext.js\";\n\nexport type ZudokuPlugin =\n | CommonPlugin\n | ProfileMenuPlugin\n | NavigationPlugin\n | ApiIdentityPlugin\n | SearchProviderPlugin\n | EventConsumerPlugin;\n\nexport type { RouteObject };\n\nexport interface NavigationPlugin {\n getRoutes: () => RouteObject[];\n getSidebar?: (path: string, context: ZudokuContext) => Promise<Sidebar>;\n}\n\nexport const createApiIdentityPlugin = (\n plugin: ApiIdentityPlugin,\n): ApiIdentityPlugin => plugin;\n\nexport const createProfileMenuPlugin = (\n plugin: ProfileMenuPlugin,\n): ProfileMenuPlugin => plugin;\n\nexport interface ApiIdentityPlugin {\n getIdentities: (context: ZudokuContext) => Promise<ApiIdentity[]>;\n}\n\nexport interface SearchProviderPlugin {\n renderSearch: (o: {\n isOpen: boolean;\n onClose: () => void;\n }) => React.JSX.Element | null;\n}\n\nexport interface ProfileMenuPlugin {\n getProfileMenuItems: (context: ZudokuContext) => ProfileNavigationItem[];\n}\n\nexport type ProfileNavigationItem = {\n label: string;\n path?: string;\n weight?: number;\n category?: \"top\" | \"middle\" | \"bottom\";\n children?: ProfileNavigationItem[];\n icon?: LucideIcon;\n};\n\nexport interface CommonPlugin {\n initialize?: (\n context: ZudokuContext,\n ) => Promise<void | boolean> | void | boolean;\n getHead?: () => ReactElement | undefined;\n getMdxComponents?: () => MdxComponentsType;\n}\n\nexport type EventConsumerPlugin<Event extends ZudokuEvents = ZudokuEvents> = {\n events: { [K in keyof Event]: Event[K] };\n};\n\nexport const isEventConsumerPlugin = (\n obj: ZudokuPlugin,\n): obj is EventConsumerPlugin =>\n \"events\" in obj && typeof obj.events === \"object\";\n\nexport const isProfileMenuPlugin = (\n obj: ZudokuPlugin,\n): obj is ProfileMenuPlugin =>\n \"getProfileMenuItems\" in obj && typeof obj.getProfileMenuItems === \"function\";\n\nexport const isNavigationPlugin = (\n obj: ZudokuPlugin,\n): obj is NavigationPlugin =>\n \"getRoutes\" in obj && typeof obj.getRoutes === \"function\";\n\nexport const isSearchPlugin = (\n obj: ZudokuPlugin,\n): obj is SearchProviderPlugin =>\n \"renderSearch\" in obj && typeof obj.renderSearch === \"function\";\n\nexport const needsInitialization = (obj: ZudokuPlugin): obj is CommonPlugin =>\n \"initialize\" in obj && typeof obj.initialize === \"function\";\n\nexport const hasHead = (obj: ZudokuPlugin): obj is CommonPlugin =>\n \"getHead\" in obj && typeof obj.getHead === \"function\";\n\nexport const isMdxProviderPlugin = (obj: ZudokuPlugin): obj is CommonPlugin =>\n \"getMdxComponents\" in obj && typeof obj.getMdxComponents === \"function\";\n\nexport const isApiIdentityPlugin = (\n obj: ZudokuPlugin,\n): obj is ApiIdentityPlugin =>\n \"getIdentities\" in obj && typeof obj.getIdentities === \"function\";\n"],"names":["createApiIdentityPlugin","plugin","createProfileMenuPlugin","isEventConsumerPlugin","obj","isProfileMenuPlugin","isNavigationPlugin","isSearchPlugin","needsInitialization","hasHead","isMdxProviderPlugin","isApiIdentityPlugin"],"mappings":"AA0Ba,MAAAA,IAA0B,CACrCC,MACsBA,GAEXC,IAA0B,CACrCD,MACsBA,GAsCXE,IAAwB,CACnCC,MAEA,YAAYA,KAAO,OAAOA,EAAI,UAAW,UAE9BC,IAAsB,CACjCD,MAEA,yBAAyBA,KAAO,OAAOA,EAAI,uBAAwB,YAExDE,IAAqB,CAChCF,MAEA,eAAeA,KAAO,OAAOA,EAAI,aAAc,YAEpCG,IAAiB,CAC5BH,MAEA,kBAAkBA,KAAO,OAAOA,EAAI,gBAAiB,YAE1CI,IAAsB,CAACJ,MAClC,gBAAgBA,KAAO,OAAOA,EAAI,cAAe,YAEtCK,IAAU,CAACL,MACtB,aAAaA,KAAO,OAAOA,EAAI,WAAY,YAEhCM,IAAsB,CAACN,MAClC,sBAAsBA,KAAO,OAAOA,EAAI,oBAAqB,YAElDO,IAAsB,CACjCP,MAEA,mBAAmBA,KAAO,OAAOA,EAAI,iBAAkB;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.35.5",
3
+ "version": "0.36.0",
4
4
  "type": "module",
5
5
  "homepage": "https://zudoku.dev",
6
6
  "repository": {
@@ -127,7 +127,7 @@
127
127
  }
128
128
  },
129
129
  "dependencies": {
130
- "@apidevtools/json-schema-ref-parser": "11.9.1",
130
+ "@apidevtools/json-schema-ref-parser": "11.9.3",
131
131
  "@envelop/core": "5.0.3",
132
132
  "@graphql-typed-document-node/core": "3.2.0",
133
133
  "@hookform/resolvers": "4.1.3",
@@ -157,7 +157,7 @@
157
157
  "@radix-ui/react-toggle-group": "1.1.2",
158
158
  "@radix-ui/react-tooltip": "1.1.8",
159
159
  "@radix-ui/react-visually-hidden": "1.1.2",
160
- "@scalar/openapi-parser": "0.10.6",
160
+ "@scalar/openapi-parser": "0.10.12",
161
161
  "@sentry/node": "9.1.0",
162
162
  "@sindresorhus/slugify": "2.2.1",
163
163
  "@stefanprobst/rehype-extract-toc": "2.2.1",
@@ -181,7 +181,7 @@
181
181
  "glob": "11.0.1",
182
182
  "graphql": "16.10.0",
183
183
  "graphql-type-json": "0.3.2",
184
- "graphql-yoga": "5.12.0",
184
+ "graphql-yoga": "5.13.2",
185
185
  "gray-matter": "4.0.3",
186
186
  "hast-util-to-jsx-runtime": "^2.3.6",
187
187
  "hast-util-to-string": "3.0.1",
@@ -240,7 +240,7 @@
240
240
  },
241
241
  "devDependencies": {
242
242
  "@graphql-codegen/cli": "5.0.5",
243
- "@graphql-codegen/client-preset": "4.6.2",
243
+ "@graphql-codegen/client-preset": "4.7.0",
244
244
  "@testing-library/react": "16.2.0",
245
245
  "@types/estree": "1.0.6",
246
246
  "@types/express": "5.0.0",
@@ -13,6 +13,7 @@ import {
13
13
  import "virtual:zudoku-theme.css";
14
14
  import "vite/modulepreload-polyfill";
15
15
  import { BootstrapStatic, ServerError } from "zudoku/components";
16
+ import { NO_DEHYDRATE } from "../lib/components/cache.js";
16
17
  import type { FileWritingResponse } from "../vite/prerender/FileWritingResponse.js";
17
18
  import "./main.css";
18
19
  import { getRoutesByConfig } from "./main.js";
@@ -115,10 +116,15 @@ export const render = async ({
115
116
  );
116
117
 
117
118
  transformStream.on("finish", () => {
119
+ const dehydrated = dehydrate(queryClient, {
120
+ shouldDehydrateQuery: (query) =>
121
+ !query.queryKey.includes(NO_DEHYDRATE),
122
+ });
123
+
118
124
  response.end(
119
125
  htmlEnd?.replace(
120
126
  "</body>",
121
- `<script>window.DATA = ${JSON.stringify(dehydrate(queryClient))}</script></body>`,
127
+ `<script>window.DATA=${JSON.stringify(dehydrated)}</script></body>`,
122
128
  ),
123
129
  );
124
130
  });
@@ -13,6 +13,7 @@ import { AuthorizationError, OAuthAuthorizationError } from "../errors.js";
13
13
  import { useAuthState, type UserProfile } from "../state.js";
14
14
 
15
15
  const CODE_VERIFIER_KEY = "code-verifier";
16
+ const STATE_KEY = "oauth-state";
16
17
 
17
18
  export interface OpenIdProviderData {
18
19
  accessToken: string;
@@ -201,16 +202,12 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
201
202
  });
202
203
 
203
204
  /**
204
- * We cannot be sure the AS supports PKCE so we're going to use state too. Use of PKCE is
205
- * backwards compatible even if the AS doesn't support it which is why we're using it regardless.
205
+ * The state parameter is used to prevent CSRF attacks and should be used in all authorization requests.
206
+ * It is independent of PKCE and should be used regardless of PKCE support.
206
207
  */
207
- if (
208
- authorizationServer.code_challenge_methods_supported?.includes("S256") !==
209
- true
210
- ) {
211
- const state = oauth.generateRandomState();
212
- authorizationUrl.searchParams.set("state", state);
213
- }
208
+ const state = oauth.generateRandomState();
209
+ sessionStorage.setItem(STATE_KEY, state);
210
+ authorizationUrl.searchParams.set("state", state);
214
211
 
215
212
  // now redirect the user to authorizationUrl.href
216
213
  location.href = authorizationUrl.href;
@@ -295,6 +292,12 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
295
292
  handleCallback = async () => {
296
293
  const url = new URL(window.location.href);
297
294
  const state = url.searchParams.get("state");
295
+ const storedState = sessionStorage.getItem(STATE_KEY);
296
+ sessionStorage.removeItem(STATE_KEY);
297
+
298
+ if (state !== storedState) {
299
+ throw new AuthorizationError("Invalid state parameter");
300
+ }
298
301
 
299
302
  // one eternity later, the user lands back on the redirect_uri
300
303
  // Authorization Code Grant Request & Response
@@ -8,11 +8,13 @@ import { useScrollToHash } from "../util/useScrollToAnchor.js";
8
8
  export const AnchorLink = (props: NavLinkProps) => {
9
9
  const location = useLocation();
10
10
  const scrollToHash = useScrollToHash();
11
- const hash = useHref(props.to).split("#")[1];
11
+ const href = useHref(props.to);
12
+ const [pathname, hash] = href.split("#");
12
13
 
13
14
  const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {
14
15
  props.onClick?.(event);
15
- if (hash !== location.hash.slice(1)) return;
16
+ if (hash !== location.hash.slice(1) || pathname !== location.pathname)
17
+ return;
16
18
 
17
19
  event.preventDefault();
18
20
  scrollToHash(hash);
@@ -1,10 +1,9 @@
1
1
  import { Helmet } from "@zudoku/react-helmet-async";
2
- import { Suspense, useEffect, useRef, type ReactNode } from "react";
3
- import { Outlet, useLocation, useNavigation } from "react-router";
2
+ import { Suspense, useEffect, type ReactNode } from "react";
3
+ import { Outlet, useNavigation } from "react-router";
4
4
  import { useSpinDelay } from "spin-delay";
5
5
  import { useScrollToAnchor } from "../util/useScrollToAnchor.js";
6
6
  import { useScrollToTop } from "../util/useScrollToTop.js";
7
- import { useViewportAnchor } from "./context/ViewportAnchorContext.js";
8
7
  import { useZudoku } from "./context/ZudokuContext.js";
9
8
  import { Header } from "./Header.js";
10
9
  import { Main } from "./Main.js";
@@ -18,29 +17,17 @@ const LoadingFallback = () => (
18
17
  );
19
18
 
20
19
  export const Layout = ({ children }: { children?: ReactNode }) => {
21
- const location = useLocation();
22
- const { setActiveAnchor } = useViewportAnchor();
23
20
  const { meta, authentication } = useZudoku();
24
21
 
25
22
  useScrollToAnchor();
26
23
  useScrollToTop();
27
24
 
28
- const previousLocationPath = useRef(location.pathname);
29
-
30
25
  useEffect(() => {
31
26
  // Initialize the authentication plugin
32
27
  authentication?.onPageLoad?.();
33
28
  }, [authentication]);
34
29
 
35
- useEffect(() => {
36
- // always reset on location change
37
- if (location.pathname !== previousLocationPath.current) {
38
- setActiveAnchor("");
39
- }
40
- previousLocationPath.current = location.pathname;
41
- }, [location.pathname, setActiveAnchor]);
42
-
43
- // Page transition is happening: https://reactrouter.com/start/framework/pending-ui#global-pending-navigation
30
+ // Page transition is happening: https://reactrouter.com/start/framework/pending-navigation
44
31
  const isNavigating = Boolean(useNavigation().location);
45
32
  const showSpinner = useSpinDelay(isNavigating, {
46
33
  delay: 300,
@@ -1,4 +1,5 @@
1
1
  import { MDXProvider } from "@mdx-js/react";
2
+ import { useQueryClient } from "@tanstack/react-query";
2
3
  import { Helmet } from "@zudoku/react-helmet-async";
3
4
  import { ThemeProvider } from "next-themes";
4
5
  import {
@@ -59,6 +60,7 @@ const ZudokoInner = memo(
59
60
  [stagger, didNavigate],
60
61
  );
61
62
  const navigation = useNavigation();
63
+ const queryClient = useQueryClient();
62
64
 
63
65
  useEffect(() => {
64
66
  if (didNavigate) {
@@ -67,7 +69,9 @@ const ZudokoInner = memo(
67
69
  setDidNavigate(true);
68
70
  }, [didNavigate, navigation.location]);
69
71
 
70
- const [zudokuContext] = useState(() => new ZudokuContext(props));
72
+ const [zudokuContext] = useState(
73
+ () => new ZudokuContext(props, queryClient),
74
+ );
71
75
 
72
76
  const heads = props.plugins
73
77
  ?.flatMap((plugin) => (hasHead(plugin) ? (plugin.getHead?.() ?? []) : []))
@@ -13,3 +13,11 @@ export const useCache = () => {
13
13
  },
14
14
  };
15
15
  };
16
+
17
+ /**
18
+ * If a query has this key in its queryKey, it will not put its result in the dehydrated state in the SSR.
19
+ *
20
+ * This is useful if the query should only be suspended and not included in the initial HTML response.
21
+ * (e.g. too large in size, or not needed for the initial page load)
22
+ */
23
+ export const NO_DEHYDRATE = "no-dehydrate";
@@ -94,14 +94,11 @@ export const ViewportAnchorProvider = ({ children }: PropsWithChildren) => {
94
94
  window.innerHeight + window.scrollY >= document.body.scrollHeight;
95
95
 
96
96
  if (hasReachedTop) {
97
- // reset the active anchor when we reach the top
98
97
  setActiveAnchor("");
99
98
  } else if (hasReachedBottom) {
100
- requestIdleCallback(() => {
101
- // set the last anchor when we reach the bottom
102
- const lastItem = Array.from(elements).pop();
103
- setActiveAnchor(lastItem?.id ?? "");
104
- });
99
+ const lastItem = Array.from(elements).pop();
100
+ const lastId = lastItem?.id ?? "";
101
+ setActiveAnchor(lastId);
105
102
  }
106
103
  };
107
104
 
@@ -2,9 +2,9 @@ import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
2
2
  import { createContext, useContext } from "react";
3
3
  import { matchPath, useLocation } from "react-router";
4
4
  import { useAuth } from "../../authentication/hook.js";
5
- import { ZudokuContext } from "../../core/ZudokuContext.js";
5
+ import type { ZudokuContext } from "../../core/ZudokuContext.js";
6
6
  import { joinPath } from "../../util/joinPath.js";
7
- import { CACHE_KEYS } from "../cache.js";
7
+ import { CACHE_KEYS, NO_DEHYDRATE } from "../cache.js";
8
8
  import { traverseSidebar } from "../navigation/utils.js";
9
9
 
10
10
  export const ZudokuReactContext = createContext<ZudokuContext | undefined>(
@@ -26,7 +26,7 @@ export const useApiIdentities = () => {
26
26
 
27
27
  return useQuery({
28
28
  queryFn: getApiIdentities,
29
- queryKey: [CACHE_KEYS.API_IDENTITIES],
29
+ queryKey: CACHE_KEYS.API_IDENTITIES,
30
30
  });
31
31
  };
32
32
 
@@ -59,7 +59,8 @@ export const useCurrentNavigation = () => {
59
59
 
60
60
  const { data } = useSuspenseQuery({
61
61
  queryFn: () => getPluginSidebar(location.pathname),
62
- queryKey: ["plugin-sidebar", location.pathname],
62
+ // We just want to suspend here and don't store in SSR dehydrated state
63
+ queryKey: ["plugin-sidebar", NO_DEHYDRATE, location.pathname],
63
64
  });
64
65
 
65
66
  const hideSidebar =
@@ -1,6 +1,6 @@
1
1
  import { cva } from "class-variance-authority";
2
2
  import { ExternalLinkIcon } from "lucide-react";
3
- import { NavLink, useSearchParams } from "react-router";
3
+ import { NavLink, useLocation, useSearchParams } from "react-router";
4
4
 
5
5
  import type { SidebarItem as SidebarItemType } from "../../../config/validators/SidebarSchema.js";
6
6
  import { joinPath } from "../../util/joinPath.js";
@@ -37,6 +37,7 @@ export const SidebarItem = ({
37
37
  item: SidebarItemType;
38
38
  onRequestClose?: () => void;
39
39
  }) => {
40
+ const location = useLocation();
40
41
  const { activeAnchor } = useViewportAnchor();
41
42
  const [searchParams] = useSearchParams();
42
43
 
@@ -76,7 +77,7 @@ export const SidebarItem = ({
76
77
  }}
77
78
  {...{ [DATA_ANCHOR_ATTR]: item.href.split("#")[1] }}
78
79
  className={navigationListItem({
79
- isActive: item.href.split("#")[1] === activeAnchor,
80
+ isActive: item.href === [location.pathname, activeAnchor].join("#"),
80
81
  className: item.badge?.placement !== "start" && "justify-between",
81
82
  })}
82
83
  onClick={onRequestClose}
@@ -1,11 +1,12 @@
1
+ import type { QueryClient } from "@tanstack/react-query";
1
2
  import { createNanoEvents } from "nanoevents";
2
- import { ReactNode } from "react";
3
- import { Location } from "react-router";
4
- import { TopNavigationItem } from "../../config/validators/common.js";
3
+ import type { ReactNode } from "react";
4
+ import type { Location } from "react-router";
5
+ import type { TopNavigationItem } from "../../config/validators/common.js";
5
6
  import type { SidebarConfig } from "../../config/validators/SidebarSchema.js";
6
- import { type AuthenticationProvider } from "../authentication/authentication.js";
7
+ import type { AuthenticationProvider } from "../authentication/authentication.js";
7
8
  import type { ComponentsContextType } from "../components/context/ComponentsContext.js";
8
- import { Slotlets } from "../components/SlotletProvider.js";
9
+ import type { Slotlets } from "../components/SlotletProvider.js";
9
10
  import { joinPath } from "../util/joinPath.js";
10
11
  import type { MdxComponentsType } from "../util/MdxComponents.js";
11
12
  import { objectEntries } from "../util/objectEntries.js";
@@ -90,7 +91,10 @@ export class ZudokuContext {
90
91
  private readonly navigationPlugins: NavigationPlugin[];
91
92
  private emitter = createNanoEvents<ZudokuEvents>();
92
93
 
93
- constructor(public readonly options: ZudokuContextOptions) {
94
+ constructor(
95
+ public readonly options: ZudokuContextOptions,
96
+ public readonly queryClient: QueryClient,
97
+ ) {
94
98
  this.plugins = options.plugins ?? [];
95
99
  this.topNavigation = options.topNavigation ?? [];
96
100
  this.sidebars = options.sidebars ?? {};
@@ -98,7 +102,6 @@ export class ZudokuContext {
98
102
  this.authentication = options.authentication;
99
103
  this.meta = options.metadata;
100
104
  this.page = options.page;
101
-
102
105
  this.plugins.forEach((plugin) => {
103
106
  if (!isEventConsumerPlugin(plugin)) return;
104
107
 
@@ -143,7 +146,7 @@ export class ZudokuContext {
143
146
  getPluginSidebar = async (path: string) => {
144
147
  const navigations = await Promise.all(
145
148
  this.navigationPlugins.map((plugin) =>
146
- plugin.getSidebar?.(joinPath(path)),
149
+ plugin.getSidebar?.(joinPath(path), this),
147
150
  ),
148
151
  );
149
152
 
@@ -2,11 +2,11 @@ import type { LucideIcon } from "lucide-react";
2
2
  import { type ReactElement } from "react";
3
3
  import { type RouteObject } from "react-router";
4
4
  import type { Sidebar } from "../../config/validators/SidebarSchema.js";
5
- import { MdxComponentsType } from "../util/MdxComponents.js";
5
+ import { type MdxComponentsType } from "../util/MdxComponents.js";
6
6
  import {
7
- ZudokuContext,
8
- ZudokuEvents,
9
7
  type ApiIdentity,
8
+ type ZudokuContext,
9
+ type ZudokuEvents,
10
10
  } from "./ZudokuContext.js";
11
11
 
12
12
  export type ZudokuPlugin =
@@ -21,7 +21,7 @@ export type { RouteObject };
21
21
 
22
22
  export interface NavigationPlugin {
23
23
  getRoutes: () => RouteObject[];
24
- getSidebar?: (path: string) => Promise<Sidebar>;
24
+ getSidebar?: (path: string, context: ZudokuContext) => Promise<Sidebar>;
25
25
  }
26
26
 
27
27
  export const createApiIdentityPlugin = (
@@ -12,8 +12,8 @@ import { useEvent } from "./useEvent.js";
12
12
  */
13
13
 
14
14
  const createTestContext = () => {
15
- const context = new ZudokuContext({});
16
15
  const queryClient = new QueryClient();
16
+ const context = new ZudokuContext({}, queryClient);
17
17
  const wrapper = ({ children }: PropsWithChildren) => (
18
18
  <QueryClientProvider client={queryClient}>
19
19
  <ZudokuProvider context={context}>{children}</ZudokuProvider>