zudoku 0.33.1 → 0.34.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.
- package/dist/config/validators/common.d.ts +572 -354
- package/dist/config/validators/common.js +26 -8
- package/dist/config/validators/common.js.map +1 -1
- package/dist/config/validators/validate.d.ts +254 -167
- package/dist/lib/authentication/hook.d.ts +1 -0
- package/dist/lib/authentication/hook.js +11 -1
- package/dist/lib/authentication/hook.js.map +1 -1
- package/dist/lib/authentication/providers/clerk.js +6 -6
- package/dist/lib/authentication/providers/clerk.js.map +1 -1
- package/dist/lib/components/AnchorLink.d.ts +2 -2
- package/dist/lib/components/AnchorLink.js +4 -4
- package/dist/lib/components/AnchorLink.js.map +1 -1
- package/dist/lib/components/Banner.js +1 -1
- package/dist/lib/components/Banner.js.map +1 -1
- package/dist/lib/components/Heading.d.ts +2 -2
- package/dist/lib/components/Layout.js +1 -1
- package/dist/lib/components/Layout.js.map +1 -1
- package/dist/lib/components/context/ZudokuContext.d.ts +1 -1
- package/dist/lib/components/index.d.ts +1 -0
- package/dist/lib/components/navigation/SidebarItem.js +6 -5
- package/dist/lib/components/navigation/SidebarItem.js.map +1 -1
- package/dist/lib/core/RouteGuard.js +2 -1
- package/dist/lib/core/RouteGuard.js.map +1 -1
- package/dist/lib/core/ZudokuContext.d.ts +4 -0
- package/dist/lib/core/ZudokuContext.js.map +1 -1
- package/dist/lib/plugins/api-catalog/Catalog.d.ts +3 -1
- package/dist/lib/plugins/api-catalog/Catalog.js +7 -4
- package/dist/lib/plugins/api-catalog/Catalog.js.map +1 -1
- package/dist/lib/plugins/api-catalog/index.js +1 -1
- package/dist/lib/plugins/api-catalog/index.js.map +1 -1
- package/dist/lib/plugins/markdown/MdxPage.js +1 -1
- package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationList.d.ts +1 -1
- package/dist/lib/plugins/openapi/OperationList.js +5 -1
- package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationListItem.d.ts +1 -1
- package/dist/lib/plugins/openapi/OperationListItem.js +6 -3
- package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
- package/dist/lib/plugins/openapi/ParameterList.d.ts +2 -1
- package/dist/lib/plugins/openapi/ParameterList.js +3 -2
- package/dist/lib/plugins/openapi/ParameterList.js.map +1 -1
- package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js +3 -1
- package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -1
- package/dist/lib/plugins/openapi/Sidecar.js +1 -1
- package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
- package/dist/lib/plugins/openapi/graphql/gql.d.ts +1 -1
- package/dist/lib/plugins/openapi/graphql/gql.js +1 -1
- package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
- package/dist/lib/plugins/openapi/graphql/graphql.d.ts +1 -0
- package/dist/lib/plugins/openapi/graphql/graphql.js +2 -0
- package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/ExamplesDropdown.d.ts +2 -2
- package/dist/lib/plugins/openapi/playground/ExamplesDropdown.js +1 -5
- package/dist/lib/plugins/openapi/playground/ExamplesDropdown.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/Headers.js +1 -1
- package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/IdentityDialog.d.ts +11 -0
- package/dist/lib/plugins/openapi/playground/IdentityDialog.js +14 -0
- package/dist/lib/plugins/openapi/playground/IdentityDialog.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/IdentitySelector.d.ts +7 -0
- package/dist/lib/plugins/openapi/playground/IdentitySelector.js +10 -0
- package/dist/lib/plugins/openapi/playground/IdentitySelector.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/PathParams.d.ts +3 -2
- package/dist/lib/plugins/openapi/playground/PathParams.js +3 -2
- package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/Playground.d.ts +13 -2
- package/dist/lib/plugins/openapi/playground/Playground.js +80 -26
- package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/QueryParams.js +1 -1
- package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/RequestLoginDialog.d.ts +7 -0
- package/dist/lib/plugins/openapi/playground/RequestLoginDialog.js +8 -0
- package/dist/lib/plugins/openapi/playground/RequestLoginDialog.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/rememberedIdentity.d.ts +17 -0
- package/dist/lib/plugins/openapi/playground/rememberedIdentity.js +11 -0
- package/dist/lib/plugins/openapi/playground/rememberedIdentity.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js +19 -13
- package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.d.ts +6 -4
- package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js +4 -3
- package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js.map +1 -1
- package/dist/lib/plugins/search-pagefind/PagefindSearch.d.ts +6 -0
- package/dist/lib/plugins/search-pagefind/PagefindSearch.js +66 -0
- package/dist/lib/plugins/search-pagefind/PagefindSearch.js.map +1 -0
- package/dist/lib/plugins/search-pagefind/ResultList.d.ts +8 -0
- package/dist/lib/plugins/search-pagefind/ResultList.js +31 -0
- package/dist/lib/plugins/search-pagefind/ResultList.js.map +1 -0
- package/dist/lib/plugins/search-pagefind/get-results.d.ts +3 -0
- package/dist/lib/plugins/search-pagefind/get-results.js +37 -0
- package/dist/lib/plugins/search-pagefind/get-results.js.map +1 -0
- package/dist/lib/plugins/search-pagefind/index.d.ts +8 -0
- package/dist/lib/plugins/search-pagefind/index.js +9 -0
- package/dist/lib/plugins/search-pagefind/index.js.map +1 -0
- package/dist/lib/plugins/search-pagefind/types.d.ts +85 -0
- package/dist/lib/plugins/search-pagefind/types.js +2 -0
- package/dist/lib/plugins/search-pagefind/types.js.map +1 -0
- package/dist/lib/ui/Checkbox.d.ts +2 -8
- package/dist/lib/ui/Checkbox.js +1 -13
- package/dist/lib/ui/Checkbox.js.map +1 -1
- package/dist/lib/ui/Command.d.ts +13 -7
- package/dist/lib/ui/Command.js +2 -2
- package/dist/lib/ui/Command.js.map +1 -1
- package/dist/lib/ui/Select.js +1 -1
- package/dist/lib/ui/Select.js.map +1 -1
- package/dist/lib/ui/SyntaxHighlight.d.ts +2 -1
- package/dist/lib/ui/SyntaxHighlight.js +19 -15
- package/dist/lib/ui/SyntaxHighlight.js.map +1 -1
- package/dist/lib/util/MdxComponents.d.ts +1 -1
- package/dist/lib/util/MdxComponents.js +2 -2
- package/dist/lib/util/MdxComponents.js.map +1 -1
- package/dist/lib/util/useScrollToAnchor.js +6 -8
- package/dist/lib/util/useScrollToAnchor.js.map +1 -1
- package/dist/vite/build.js +4 -0
- package/dist/vite/build.js.map +1 -1
- package/dist/vite/config.js +7 -2
- package/dist/vite/config.js.map +1 -1
- package/dist/vite/dev-server.js +8 -0
- package/dist/vite/dev-server.js.map +1 -1
- package/dist/vite/pagefind.d.ts +4 -0
- package/dist/vite/pagefind.js +15 -0
- package/dist/vite/pagefind.js.map +1 -0
- package/dist/vite/plugin-component.js +4 -0
- package/dist/vite/plugin-component.js.map +1 -1
- package/dist/vite/plugin-search.js +4 -0
- package/dist/vite/plugin-search.js.map +1 -1
- package/dist/vite/prerender/prerender.js +1 -1
- package/dist/vite/prerender/prerender.js.map +1 -1
- package/dist/vite/sitemap.js +2 -1
- package/dist/vite/sitemap.js.map +1 -1
- package/lib/{AuthenticationPlugin-CiO1FM6Q.js → AuthenticationPlugin-4ip08maU.js} +3 -3
- package/lib/{AuthenticationPlugin-CiO1FM6Q.js.map → AuthenticationPlugin-4ip08maU.js.map} +1 -1
- package/lib/Callout-B_sEhkYd.js +211 -0
- package/lib/Callout-B_sEhkYd.js.map +1 -0
- package/lib/{Dialog-DIKGQxQc.js → Dialog-sbgekbjb.js} +47 -32
- package/lib/{Dialog-DIKGQxQc.js.map → Dialog-sbgekbjb.js.map} +1 -1
- package/lib/{Markdown-DePfm7oQ.js → Markdown-DZXjQjpH.js} +4099 -3848
- package/lib/Markdown-DZXjQjpH.js.map +1 -0
- package/lib/MdxPage-52vRwa_7.js +200 -0
- package/lib/MdxPage-52vRwa_7.js.map +1 -0
- package/lib/{OasProvider-SzD9mHJc.js → OasProvider-CR2nG1Eg.js} +4 -4
- package/lib/{OasProvider-SzD9mHJc.js.map → OasProvider-CR2nG1Eg.js.map} +1 -1
- package/lib/{OperationList-DDs9NblY.js → OperationList-DndcCJUG.js} +2069 -1983
- package/lib/OperationList-DndcCJUG.js.map +1 -0
- package/lib/{Select-Dqtcn53H.js → Select-FAYHOYTy.js} +4 -4
- package/lib/{Select-Dqtcn53H.js.map → Select-FAYHOYTy.js.map} +1 -1
- package/lib/{SlotletProvider-DdtIOUi6.js → SlotletProvider-TydSHROc.js} +4 -4
- package/lib/{SlotletProvider-DdtIOUi6.js.map → SlotletProvider-TydSHROc.js.map} +1 -1
- package/lib/{chunk-IR6S3I6Y-D_3UmFIn.js → chunk-HA7DTUK3-ZGg2W6yV.js} +277 -277
- package/lib/chunk-HA7DTUK3-ZGg2W6yV.js.map +1 -0
- package/lib/hook-CfCFKZ-2.js +350 -0
- package/lib/hook-CfCFKZ-2.js.map +1 -0
- package/lib/index-DK7IuUyR.js +2201 -0
- package/lib/index-DK7IuUyR.js.map +1 -0
- package/lib/{index.esm-CQHE3GEU.js → index.esm-CltAN0Tf.js} +259 -239
- package/lib/index.esm-CltAN0Tf.js.map +1 -0
- package/lib/{mutation-EclmI0is.js → mutation-B81DztCT.js} +2 -2
- package/lib/{mutation-EclmI0is.js.map → mutation-B81DztCT.js.map} +1 -1
- package/lib/objectEntries-BS7aAgOm.js +12 -0
- package/lib/objectEntries-BS7aAgOm.js.map +1 -0
- package/lib/ui/Checkbox.js +15 -25
- package/lib/ui/Checkbox.js.map +1 -1
- package/lib/ui/Command.js +96 -70
- package/lib/ui/Command.js.map +1 -1
- package/lib/ui/Select.js +1 -1
- package/lib/ui/Select.js.map +1 -1
- package/lib/ui/SyntaxHighlight.js +483 -502
- package/lib/ui/SyntaxHighlight.js.map +1 -1
- package/lib/{useExposedProps-RIvey2Oy.js → useExposedProps-BslIn-FE.js} +2 -2
- package/lib/{useExposedProps-RIvey2Oy.js.map → useExposedProps-BslIn-FE.js.map} +1 -1
- package/lib/useQuery-CQUwWR9i.js +1137 -0
- package/lib/useQuery-CQUwWR9i.js.map +1 -0
- package/lib/zudoku.auth-auth0.js +1 -1
- package/lib/zudoku.auth-clerk.js +29 -29
- package/lib/zudoku.auth-clerk.js.map +1 -1
- package/lib/zudoku.auth-openid.js +3 -3
- package/lib/zudoku.components.js +146 -149
- package/lib/zudoku.components.js.map +1 -1
- package/lib/zudoku.hooks.js +1 -1
- package/lib/zudoku.plugin-api-catalog.js +87 -71
- package/lib/zudoku.plugin-api-catalog.js.map +1 -1
- package/lib/zudoku.plugin-api-keys.js +16 -15
- package/lib/zudoku.plugin-api-keys.js.map +1 -1
- package/lib/zudoku.plugin-custom-pages.js +2 -2
- package/lib/zudoku.plugin-markdown.js +1 -1
- package/lib/zudoku.plugin-openapi.js +3 -3
- package/lib/zudoku.plugin-redirect.js +1 -1
- package/lib/zudoku.plugin-search-pagefind.js +204 -0
- package/lib/zudoku.plugin-search-pagefind.js.map +1 -0
- package/package.json +10 -5
- package/src/lib/authentication/hook.ts +12 -1
- package/src/lib/authentication/providers/clerk.tsx +10 -6
- package/src/lib/components/AnchorLink.tsx +7 -7
- package/src/lib/components/Banner.tsx +1 -0
- package/src/lib/components/Heading.tsx +1 -1
- package/src/lib/components/Layout.tsx +1 -0
- package/src/lib/components/navigation/SidebarItem.tsx +8 -23
- package/src/lib/core/RouteGuard.tsx +2 -1
- package/src/lib/core/ZudokuContext.ts +4 -0
- package/src/lib/plugins/api-catalog/Catalog.tsx +23 -7
- package/src/lib/plugins/api-catalog/index.tsx +1 -0
- package/src/lib/plugins/markdown/MdxPage.tsx +5 -1
- package/src/lib/plugins/openapi/OperationList.tsx +83 -31
- package/src/lib/plugins/openapi/OperationListItem.tsx +107 -86
- package/src/lib/plugins/openapi/ParameterList.tsx +4 -0
- package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +7 -0
- package/src/lib/plugins/openapi/Sidecar.tsx +1 -0
- package/src/lib/plugins/openapi/graphql/gql.ts +3 -3
- package/src/lib/plugins/openapi/graphql/graphql.ts +3 -0
- package/src/lib/plugins/openapi/playground/ExamplesDropdown.tsx +30 -32
- package/src/lib/plugins/openapi/playground/Headers.tsx +0 -1
- package/src/lib/plugins/openapi/playground/IdentityDialog.tsx +74 -0
- package/src/lib/plugins/openapi/playground/IdentitySelector.tsx +54 -0
- package/src/lib/plugins/openapi/playground/PathParams.tsx +8 -2
- package/src/lib/plugins/openapi/playground/Playground.tsx +175 -88
- package/src/lib/plugins/openapi/playground/QueryParams.tsx +0 -1
- package/src/lib/plugins/openapi/playground/RequestLoginDialog.tsx +51 -0
- package/src/lib/plugins/openapi/playground/rememberedIdentity.ts +26 -0
- package/src/lib/plugins/openapi/playground/result-panel/ResponseTab.tsx +24 -4
- package/src/lib/plugins/openapi/playground/result-panel/ResultPanel.tsx +66 -45
- package/src/lib/plugins/search-pagefind/PagefindSearch.tsx +135 -0
- package/src/lib/plugins/search-pagefind/ResultList.tsx +104 -0
- package/src/lib/plugins/search-pagefind/get-results.tsx +54 -0
- package/src/lib/plugins/search-pagefind/index.tsx +21 -0
- package/src/lib/plugins/search-pagefind/types.ts +118 -0
- package/src/lib/ui/Checkbox.tsx +8 -24
- package/src/lib/ui/Command.tsx +25 -3
- package/src/lib/ui/Select.tsx +1 -1
- package/src/lib/ui/SyntaxHighlight.tsx +94 -96
- package/src/lib/util/MdxComponents.tsx +2 -2
- package/src/lib/util/useScrollToAnchor.ts +8 -8
- package/lib/Markdown-DePfm7oQ.js.map +0 -1
- package/lib/MdxPage-DZTt9ld7.js +0 -193
- package/lib/MdxPage-DZTt9ld7.js.map +0 -1
- package/lib/OperationList-DDs9NblY.js.map +0 -1
- package/lib/chunk-IR6S3I6Y-D_3UmFIn.js.map +0 -1
- package/lib/hook-CN__aZIt.js +0 -1464
- package/lib/hook-CN__aZIt.js.map +0 -1
- package/lib/index-CibzSNks.js +0 -2100
- package/lib/index-CibzSNks.js.map +0 -1
- package/lib/index.esm-CQHE3GEU.js.map +0 -1
- package/lib/objectEntries-yMIkr2mI.js +0 -5
- package/lib/objectEntries-yMIkr2mI.js.map +0 -1
- package/lib/useScrollToAnchor-C7ilRSts.js +0 -290
- package/lib/useScrollToAnchor-C7ilRSts.js.map +0 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Card } from "zudoku/ui/Card.js";
|
|
2
|
+
import { Label } from "zudoku/ui/Label.js";
|
|
3
|
+
import { RadioGroup, RadioGroupItem } from "zudoku/ui/RadioGroup.js";
|
|
4
|
+
import { type ApiIdentity } from "../../../core/ZudokuContext.js";
|
|
5
|
+
import { NO_IDENTITY } from "./Playground.js";
|
|
6
|
+
|
|
7
|
+
const IdentitySelector = ({
|
|
8
|
+
identities,
|
|
9
|
+
setValue,
|
|
10
|
+
value,
|
|
11
|
+
}: {
|
|
12
|
+
identities?: ApiIdentity[];
|
|
13
|
+
setValue: (value: string) => void;
|
|
14
|
+
value?: string;
|
|
15
|
+
}) => {
|
|
16
|
+
return (
|
|
17
|
+
<Card className="w-full overflow-hidden">
|
|
18
|
+
<RadioGroup
|
|
19
|
+
onValueChange={(value) => setValue(value)}
|
|
20
|
+
value={value}
|
|
21
|
+
defaultValue={NO_IDENTITY}
|
|
22
|
+
className="gap-0"
|
|
23
|
+
disabled={identities?.length === 0}
|
|
24
|
+
>
|
|
25
|
+
<Label
|
|
26
|
+
className="h-12 border-b items-center flex p-4 cursor-pointer hover:bg-accent"
|
|
27
|
+
htmlFor="none"
|
|
28
|
+
>
|
|
29
|
+
<RadioGroupItem value={NO_IDENTITY} id="none">
|
|
30
|
+
None
|
|
31
|
+
</RadioGroupItem>
|
|
32
|
+
<Label htmlFor="none" className="ml-2">
|
|
33
|
+
None
|
|
34
|
+
</Label>
|
|
35
|
+
</Label>
|
|
36
|
+
{identities?.map((identity) => (
|
|
37
|
+
<Label
|
|
38
|
+
key={identity.id}
|
|
39
|
+
className="h-12 border-b items-center flex p-4 cursor-pointer hover:bg-accent"
|
|
40
|
+
>
|
|
41
|
+
<RadioGroupItem value={identity.id} id={identity.id}>
|
|
42
|
+
{identity.label}
|
|
43
|
+
</RadioGroupItem>
|
|
44
|
+
<Label htmlFor={identity.id} className="ml-2">
|
|
45
|
+
{identity.label}
|
|
46
|
+
</Label>
|
|
47
|
+
</Label>
|
|
48
|
+
))}
|
|
49
|
+
</RadioGroup>
|
|
50
|
+
</Card>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default IdentitySelector;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Control, Controller, useFieldArray } from "react-hook-form";
|
|
1
|
+
import { type Control, Controller, useFieldArray } from "react-hook-form";
|
|
2
2
|
import { Card } from "zudoku/ui/Card.js";
|
|
3
3
|
import { Input } from "../../../ui/Input.js";
|
|
4
4
|
import { ColorizedParam } from "../ColorizedParam.js";
|
|
@@ -7,18 +7,24 @@ import type { PlaygroundForm } from "./Playground.js";
|
|
|
7
7
|
|
|
8
8
|
export const PathParams = ({
|
|
9
9
|
control,
|
|
10
|
+
url,
|
|
10
11
|
}: {
|
|
11
12
|
control: Control<PlaygroundForm>;
|
|
13
|
+
url: string;
|
|
12
14
|
}) => {
|
|
13
15
|
const { fields } = useFieldArray<PlaygroundForm, "pathParams">({
|
|
14
16
|
control,
|
|
15
17
|
name: "pathParams",
|
|
16
18
|
});
|
|
17
19
|
|
|
20
|
+
const sortedFields = [...fields].sort(
|
|
21
|
+
(a, b) => url.indexOf(`{${a.name}}`) - url.indexOf(`{${b.name}}`),
|
|
22
|
+
);
|
|
23
|
+
|
|
18
24
|
return (
|
|
19
25
|
<Card className="rounded-lg">
|
|
20
26
|
<ParamsGrid>
|
|
21
|
-
{
|
|
27
|
+
{sortedFields.map((field, i) => (
|
|
22
28
|
<ParamsGridItem key={field.id}>
|
|
23
29
|
<Controller
|
|
24
30
|
control={control}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { useMutation } from "@tanstack/react-query";
|
|
2
2
|
import { InfoIcon } from "lucide-react";
|
|
3
|
-
import { Fragment, useEffect, useRef, useTransition } from "react";
|
|
3
|
+
import { Fragment, useEffect, useRef, useState, useTransition } from "react";
|
|
4
4
|
import { FormProvider, useForm } from "react-hook-form";
|
|
5
5
|
import { Alert, AlertDescription, AlertTitle } from "zudoku/ui/Alert.js";
|
|
6
6
|
import { PathRenderer } from "../../../components/PathRenderer.js";
|
|
7
7
|
|
|
8
|
-
import { Label } from "zudoku/ui/Label.js";
|
|
9
|
-
import { RadioGroup, RadioGroupItem } from "zudoku/ui/RadioGroup.js";
|
|
10
8
|
import {
|
|
11
9
|
Select,
|
|
12
10
|
SelectContent,
|
|
@@ -17,16 +15,21 @@ import {
|
|
|
17
15
|
import { Textarea } from "zudoku/ui/Textarea.js";
|
|
18
16
|
import { useSelectedServer } from "../../../authentication/state.js";
|
|
19
17
|
import { useApiIdentities } from "../../../components/context/ZudokuContext.js";
|
|
20
|
-
import { Card } from "../../../ui/Card.js";
|
|
21
18
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../../ui/Tabs.js";
|
|
22
19
|
import { cn } from "../../../util/cn.js";
|
|
20
|
+
import { objectEntries } from "../../../util/objectEntries.js";
|
|
21
|
+
import { useLatest } from "../../../util/useLatest.js";
|
|
23
22
|
import { ColorizedParam } from "../ColorizedParam.js";
|
|
24
|
-
import { Content } from "../SidecarExamples.js";
|
|
23
|
+
import { type Content } from "../SidecarExamples.js";
|
|
25
24
|
import { createUrl } from "./createUrl.js";
|
|
26
25
|
import ExamplesDropdown from "./ExamplesDropdown.js";
|
|
27
26
|
import { Headers } from "./Headers.js";
|
|
27
|
+
import { IdentityDialog } from "./IdentityDialog.js";
|
|
28
|
+
import IdentitySelector from "./IdentitySelector.js";
|
|
28
29
|
import { PathParams } from "./PathParams.js";
|
|
29
30
|
import { QueryParams } from "./QueryParams.js";
|
|
31
|
+
import { useIdentityStore } from "./rememberedIdentity.js";
|
|
32
|
+
import RequestLoginDialog from "./RequestLoginDialog.js";
|
|
30
33
|
import { ResultPanel } from "./result-panel/ResultPanel.js";
|
|
31
34
|
import SubmitButton from "./SubmitButton.js";
|
|
32
35
|
|
|
@@ -55,8 +58,17 @@ export type PathParam = {
|
|
|
55
58
|
isRequired?: boolean;
|
|
56
59
|
};
|
|
57
60
|
|
|
61
|
+
const bodyContentTypeMap = {
|
|
62
|
+
Plain: "text/plain",
|
|
63
|
+
JSON: "application/json",
|
|
64
|
+
XML: "application/xml",
|
|
65
|
+
YAML: "application/yaml",
|
|
66
|
+
CSV: "text/csv",
|
|
67
|
+
} as const;
|
|
68
|
+
|
|
58
69
|
export type PlaygroundForm = {
|
|
59
70
|
body: string;
|
|
71
|
+
bodyContentType: keyof typeof bodyContentTypeMap;
|
|
60
72
|
queryParams: Array<{
|
|
61
73
|
name: string;
|
|
62
74
|
value: string;
|
|
@@ -97,6 +109,9 @@ export type PlaygroundContentProps = {
|
|
|
97
109
|
pathParams?: PathParam[];
|
|
98
110
|
defaultBody?: string;
|
|
99
111
|
examples?: Content;
|
|
112
|
+
requiresLogin?: boolean;
|
|
113
|
+
onLogin?: () => void;
|
|
114
|
+
onSignUp?: () => void;
|
|
100
115
|
};
|
|
101
116
|
|
|
102
117
|
export const Playground = ({
|
|
@@ -109,15 +124,27 @@ export const Playground = ({
|
|
|
109
124
|
pathParams = [],
|
|
110
125
|
defaultBody = "",
|
|
111
126
|
examples,
|
|
127
|
+
requiresLogin = false,
|
|
128
|
+
onLogin,
|
|
129
|
+
onSignUp,
|
|
112
130
|
}: PlaygroundContentProps) => {
|
|
113
131
|
const { selectedServer, setSelectedServer } = useSelectedServer(
|
|
114
132
|
servers.map((url) => ({ url })),
|
|
115
133
|
);
|
|
134
|
+
const [showSelectIdentity, setShowSelectIdentity] = useState(false);
|
|
135
|
+
const identities = useApiIdentities();
|
|
136
|
+
const { setRememberedIdentity, getRememberedIdentity } = useIdentityStore();
|
|
116
137
|
const [, startTransition] = useTransition();
|
|
138
|
+
const [skipLogin, setSkipLogin] = useState(false);
|
|
139
|
+
const [showLongRunningWarning, setShowLongRunningWarning] = useState(false);
|
|
140
|
+
const abortControllerRef = useRef<AbortController | undefined>(undefined);
|
|
141
|
+
const latestSetRememberedIdentity = useLatest(setRememberedIdentity);
|
|
142
|
+
|
|
117
143
|
const { register, control, handleSubmit, watch, setValue, ...form } =
|
|
118
144
|
useForm<PlaygroundForm>({
|
|
119
145
|
defaultValues: {
|
|
120
146
|
body: defaultBody,
|
|
147
|
+
bodyContentType: "JSON",
|
|
121
148
|
queryParams: queryParams
|
|
122
149
|
.map((param) => ({
|
|
123
150
|
name: param.name,
|
|
@@ -150,36 +177,42 @@ export const Playground = ({
|
|
|
150
177
|
active: false,
|
|
151
178
|
},
|
|
152
179
|
]),
|
|
153
|
-
identity:
|
|
180
|
+
identity: getRememberedIdentity(
|
|
181
|
+
identities.data?.map((i) => i.id) ?? [],
|
|
182
|
+
),
|
|
154
183
|
},
|
|
155
184
|
});
|
|
156
185
|
const formState = watch();
|
|
157
|
-
const
|
|
186
|
+
const formRef = useRef<HTMLFormElement>(null);
|
|
158
187
|
|
|
159
|
-
const setOnce = useRef(false);
|
|
160
188
|
useEffect(() => {
|
|
161
|
-
if (
|
|
162
|
-
|
|
163
|
-
if (firstIdentity) {
|
|
164
|
-
setValue("identity", firstIdentity.id);
|
|
165
|
-
setOnce.current = true;
|
|
189
|
+
if (formState.identity) {
|
|
190
|
+
latestSetRememberedIdentity.current(formState.identity);
|
|
166
191
|
}
|
|
167
|
-
}, [
|
|
168
|
-
|
|
169
|
-
const formRef = useRef<HTMLFormElement>(null);
|
|
192
|
+
}, [latestSetRememberedIdentity, formState.identity]);
|
|
170
193
|
|
|
171
194
|
const queryMutation = useMutation({
|
|
172
195
|
mutationFn: async (data: PlaygroundForm) => {
|
|
173
196
|
const start = performance.now();
|
|
197
|
+
|
|
198
|
+
const shouldSetContentType = !data.headers.some(
|
|
199
|
+
(h) => h.active && h.name.toLowerCase() === "content-type",
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const headers = Object.fromEntries([
|
|
203
|
+
...data.headers
|
|
204
|
+
.filter((h) => h.name && h.active)
|
|
205
|
+
.map((header) => [header.name, header.value]),
|
|
206
|
+
...(shouldSetContentType
|
|
207
|
+
? [["content-type", bodyContentTypeMap[data.bodyContentType]]]
|
|
208
|
+
: []),
|
|
209
|
+
]);
|
|
210
|
+
|
|
174
211
|
const request = new Request(
|
|
175
212
|
createUrl(server ?? selectedServer, url, data),
|
|
176
213
|
{
|
|
177
214
|
method: method.toUpperCase(),
|
|
178
|
-
headers
|
|
179
|
-
data.headers
|
|
180
|
-
.filter((h) => h.name && h.active)
|
|
181
|
-
.map((header) => [header.name, header.value]),
|
|
182
|
-
),
|
|
215
|
+
headers,
|
|
183
216
|
body: data.body ? data.body : undefined,
|
|
184
217
|
},
|
|
185
218
|
);
|
|
@@ -189,15 +222,23 @@ export const Playground = ({
|
|
|
189
222
|
?.find((i) => i.id === data.identity)
|
|
190
223
|
?.authorizeRequest(request);
|
|
191
224
|
}
|
|
225
|
+
|
|
226
|
+
const warningTimeout = setTimeout(
|
|
227
|
+
() => setShowLongRunningWarning(true),
|
|
228
|
+
3210,
|
|
229
|
+
);
|
|
230
|
+
abortControllerRef.current = new AbortController();
|
|
231
|
+
|
|
192
232
|
try {
|
|
193
233
|
const response = await fetch(request, {
|
|
194
|
-
signal:
|
|
234
|
+
signal: abortControllerRef.current.signal,
|
|
195
235
|
});
|
|
196
236
|
|
|
197
|
-
|
|
237
|
+
clearTimeout(warningTimeout);
|
|
238
|
+
setShowLongRunningWarning(false);
|
|
198
239
|
|
|
240
|
+
const time = performance.now() - start;
|
|
199
241
|
const body = await response.text();
|
|
200
|
-
|
|
201
242
|
const url = new URL(request.url);
|
|
202
243
|
|
|
203
244
|
return {
|
|
@@ -218,6 +259,8 @@ export const Playground = ({
|
|
|
218
259
|
},
|
|
219
260
|
} satisfies PlaygroundResult;
|
|
220
261
|
} catch (error) {
|
|
262
|
+
clearTimeout(warningTimeout);
|
|
263
|
+
setShowLongRunningWarning(false);
|
|
221
264
|
if (error instanceof TypeError) {
|
|
222
265
|
throw new Error(
|
|
223
266
|
"The request failed, possibly due to network issues or CORS policy.",
|
|
@@ -229,6 +272,12 @@ export const Playground = ({
|
|
|
229
272
|
},
|
|
230
273
|
});
|
|
231
274
|
|
|
275
|
+
useEffect(() => {
|
|
276
|
+
return () => {
|
|
277
|
+
abortControllerRef.current?.abort();
|
|
278
|
+
};
|
|
279
|
+
}, []);
|
|
280
|
+
|
|
232
281
|
const path = (
|
|
233
282
|
<PathRenderer
|
|
234
283
|
path={url}
|
|
@@ -264,7 +313,7 @@ export const Playground = ({
|
|
|
264
313
|
const serverSelect = (
|
|
265
314
|
<div className="inline-block opacity-50 hover:opacity-100 transition">
|
|
266
315
|
{server ? (
|
|
267
|
-
<span>{server.replace(/^https?:\/\//, "")}</span>
|
|
316
|
+
<span>{server.replace(/^https?:\/\//, "").replace(/\/$/, "")}</span>
|
|
268
317
|
) : (
|
|
269
318
|
servers.length > 1 && (
|
|
270
319
|
<Select
|
|
@@ -274,13 +323,13 @@ export const Playground = ({
|
|
|
274
323
|
value={selectedServer}
|
|
275
324
|
defaultValue={selectedServer}
|
|
276
325
|
>
|
|
277
|
-
<SelectTrigger className="p-0 border-none flex-row-reverse bg-transparent text-xs gap-0.5 h-auto">
|
|
326
|
+
<SelectTrigger className="p-0 border-none flex-row-reverse bg-transparent text-xs gap-0.5 h-auto translate-y-[4px]">
|
|
278
327
|
<SelectValue />
|
|
279
328
|
</SelectTrigger>
|
|
280
329
|
<SelectContent>
|
|
281
330
|
{servers.map((s) => (
|
|
282
331
|
<SelectItem key={s} value={s}>
|
|
283
|
-
{s.replace(/^https?:\/\//, "")}
|
|
332
|
+
{s.replace(/^https?:\/\//, "").replace(/\/$/, "")}
|
|
284
333
|
</SelectItem>
|
|
285
334
|
))}
|
|
286
335
|
</SelectContent>
|
|
@@ -290,14 +339,45 @@ export const Playground = ({
|
|
|
290
339
|
</div>
|
|
291
340
|
);
|
|
292
341
|
|
|
342
|
+
const showLogin = requiresLogin && !skipLogin;
|
|
343
|
+
const isBodySupported = ["POST", "PUT", "PATCH", "DELETE"].includes(
|
|
344
|
+
method.toUpperCase(),
|
|
345
|
+
);
|
|
346
|
+
|
|
293
347
|
return (
|
|
294
348
|
<FormProvider
|
|
295
349
|
{...{ register, control, handleSubmit, watch, setValue, ...form }}
|
|
296
350
|
>
|
|
297
351
|
<form
|
|
298
|
-
onSubmit={handleSubmit((data) =>
|
|
352
|
+
onSubmit={handleSubmit((data) => {
|
|
353
|
+
if (identities.data?.length === 0 || data.identity) {
|
|
354
|
+
queryMutation.mutate(data);
|
|
355
|
+
} else {
|
|
356
|
+
setShowSelectIdentity(true);
|
|
357
|
+
}
|
|
358
|
+
})}
|
|
299
359
|
ref={formRef}
|
|
360
|
+
className="relative"
|
|
300
361
|
>
|
|
362
|
+
<IdentityDialog
|
|
363
|
+
identities={identities.data ?? []}
|
|
364
|
+
open={showSelectIdentity}
|
|
365
|
+
onOpenChange={setShowSelectIdentity}
|
|
366
|
+
onSubmit={({ rememberedIdentity, identity }) => {
|
|
367
|
+
if (rememberedIdentity) {
|
|
368
|
+
setValue("identity", identity ?? NO_IDENTITY);
|
|
369
|
+
}
|
|
370
|
+
setShowSelectIdentity(false);
|
|
371
|
+
queryMutation.mutate({ ...formState, identity });
|
|
372
|
+
}}
|
|
373
|
+
/>
|
|
374
|
+
<RequestLoginDialog
|
|
375
|
+
open={showLogin}
|
|
376
|
+
setOpen={(open) => setSkipLogin(!open)}
|
|
377
|
+
onSignUp={onSignUp}
|
|
378
|
+
onLogin={onLogin}
|
|
379
|
+
/>
|
|
380
|
+
|
|
301
381
|
<div className="grid grid-cols-2 text-sm h-full">
|
|
302
382
|
<div className="flex flex-col gap-4 p-4 after:bg-muted-foreground/20 relative after:absolute after:w-px after:inset-0 after:left-auto">
|
|
303
383
|
<div className="flex gap-2 items-stretch">
|
|
@@ -305,7 +385,7 @@ export const Playground = ({
|
|
|
305
385
|
<div className="border-r p-2 bg-muted rounded-l-md self-stretch font-semibold font-mono flex items-center">
|
|
306
386
|
{method.toUpperCase()}
|
|
307
387
|
</div>
|
|
308
|
-
<div className="items-center
|
|
388
|
+
<div className="items-center px-2 py-0.5 font-mono text-xs break-all leading-6">
|
|
309
389
|
{serverSelect}
|
|
310
390
|
{path}
|
|
311
391
|
{urlQueryParams.length > 0 ? "?" : ""}
|
|
@@ -316,7 +396,7 @@ export const Playground = ({
|
|
|
316
396
|
<SubmitButton
|
|
317
397
|
identities={identities.data ?? []}
|
|
318
398
|
formRef={formRef}
|
|
319
|
-
disabled={form.formState.isSubmitting}
|
|
399
|
+
disabled={identities.isLoading || form.formState.isSubmitting}
|
|
320
400
|
/>
|
|
321
401
|
</div>
|
|
322
402
|
<Tabs defaultValue="parameters">
|
|
@@ -341,7 +421,12 @@ export const Playground = ({
|
|
|
341
421
|
<div className="w-2 h-2 rounded-full bg-blue-400 ml-2" />
|
|
342
422
|
)}
|
|
343
423
|
</TabsTrigger>
|
|
344
|
-
<TabsTrigger value="body">
|
|
424
|
+
<TabsTrigger value="body">
|
|
425
|
+
Body
|
|
426
|
+
{formState.body && (
|
|
427
|
+
<div className="w-2 h-2 rounded-full bg-blue-400 ml-2" />
|
|
428
|
+
)}
|
|
429
|
+
</TabsTrigger>
|
|
345
430
|
</TabsList>
|
|
346
431
|
</div>
|
|
347
432
|
<TabsContent value="headers">
|
|
@@ -351,7 +436,7 @@ export const Playground = ({
|
|
|
351
436
|
{pathParams.length > 0 && (
|
|
352
437
|
<div className="flex flex-col gap-4 my-4">
|
|
353
438
|
<span className="font-semibold">Path Parameters</span>
|
|
354
|
-
<PathParams control={control} />
|
|
439
|
+
<PathParams url={url} control={control} />
|
|
355
440
|
</div>
|
|
356
441
|
)}
|
|
357
442
|
<div className="flex flex-col gap-4 my-4">
|
|
@@ -375,31 +460,58 @@ export const Playground = ({
|
|
|
375
460
|
<Textarea
|
|
376
461
|
{...register("body")}
|
|
377
462
|
className={cn(
|
|
378
|
-
"border w-full rounded-lg p-2
|
|
379
|
-
!
|
|
380
|
-
method.toUpperCase(),
|
|
381
|
-
) && "h-20",
|
|
463
|
+
"border w-full rounded-lg bg-muted/40 p-2 h-64 font-mono text-[13px]",
|
|
464
|
+
!isBodySupported && "h-20 bg-muted",
|
|
382
465
|
)}
|
|
383
466
|
placeholder={
|
|
384
|
-
!
|
|
385
|
-
method.toUpperCase(),
|
|
386
|
-
)
|
|
467
|
+
!isBodySupported
|
|
387
468
|
? "This request does not support a body"
|
|
388
469
|
: undefined
|
|
389
470
|
}
|
|
390
|
-
disabled={
|
|
391
|
-
!["POST", "PUT", "PATCH", "DELETE"].includes(
|
|
392
|
-
method.toUpperCase(),
|
|
393
|
-
)
|
|
394
|
-
}
|
|
471
|
+
disabled={!isBodySupported}
|
|
395
472
|
/>
|
|
396
|
-
{
|
|
397
|
-
<
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
473
|
+
{isBodySupported && (
|
|
474
|
+
<div className="flex items-center gap-2 mt-2 justify-between">
|
|
475
|
+
<Select
|
|
476
|
+
value={formState.bodyContentType}
|
|
477
|
+
onValueChange={(value) =>
|
|
478
|
+
setValue(
|
|
479
|
+
"bodyContentType",
|
|
480
|
+
value as keyof typeof bodyContentTypeMap,
|
|
481
|
+
)
|
|
482
|
+
}
|
|
483
|
+
>
|
|
484
|
+
<SelectTrigger className="w-[100px]">
|
|
485
|
+
<SelectValue />
|
|
486
|
+
</SelectTrigger>
|
|
487
|
+
<SelectContent>
|
|
488
|
+
{Object.keys(bodyContentTypeMap).map((format) => (
|
|
489
|
+
<SelectItem key={format} value={format}>
|
|
490
|
+
{format}
|
|
491
|
+
</SelectItem>
|
|
492
|
+
))}
|
|
493
|
+
</SelectContent>
|
|
494
|
+
</Select>
|
|
495
|
+
{examples && examples.length > 0 && (
|
|
496
|
+
<ExamplesDropdown
|
|
497
|
+
examples={examples}
|
|
498
|
+
onSelect={(example, mediaType) => {
|
|
499
|
+
setValue(
|
|
500
|
+
"body",
|
|
501
|
+
JSON.stringify(example.value, null, 2),
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
const format = objectEntries(bodyContentTypeMap).find(
|
|
505
|
+
([_, contentType]) => contentType === mediaType,
|
|
506
|
+
)?.[0];
|
|
507
|
+
|
|
508
|
+
if (format) {
|
|
509
|
+
setValue("bodyContentType", format);
|
|
510
|
+
}
|
|
511
|
+
}}
|
|
512
|
+
/>
|
|
513
|
+
)}
|
|
514
|
+
</div>
|
|
403
515
|
)}
|
|
404
516
|
</TabsContent>
|
|
405
517
|
<TabsContent value="auth">
|
|
@@ -414,43 +526,11 @@ export const Playground = ({
|
|
|
414
526
|
</Alert>
|
|
415
527
|
)}
|
|
416
528
|
<div className="flex flex-col items-center gap-2">
|
|
417
|
-
<
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
className="gap-0"
|
|
423
|
-
disabled={identities.data?.length === 0}
|
|
424
|
-
>
|
|
425
|
-
<Label
|
|
426
|
-
className="h-12 border-b items-center flex p-4 cursor-pointer hover:bg-accent"
|
|
427
|
-
htmlFor="none"
|
|
428
|
-
>
|
|
429
|
-
<RadioGroupItem value={NO_IDENTITY} id="none">
|
|
430
|
-
None
|
|
431
|
-
</RadioGroupItem>
|
|
432
|
-
<Label htmlFor="none" className="ml-2">
|
|
433
|
-
None
|
|
434
|
-
</Label>
|
|
435
|
-
</Label>
|
|
436
|
-
{identities.data?.map((identity) => (
|
|
437
|
-
<Label
|
|
438
|
-
key={identity.id}
|
|
439
|
-
className="h-12 border-b items-center flex p-4 cursor-pointer hover:bg-accent"
|
|
440
|
-
>
|
|
441
|
-
<RadioGroupItem
|
|
442
|
-
value={identity.id}
|
|
443
|
-
id={identity.id}
|
|
444
|
-
>
|
|
445
|
-
{identity.label}
|
|
446
|
-
</RadioGroupItem>
|
|
447
|
-
<Label htmlFor={identity.id} className="ml-2">
|
|
448
|
-
{identity.label}
|
|
449
|
-
</Label>
|
|
450
|
-
</Label>
|
|
451
|
-
))}
|
|
452
|
-
</RadioGroup>
|
|
453
|
-
</Card>
|
|
529
|
+
<IdentitySelector
|
|
530
|
+
value={formState.identity}
|
|
531
|
+
identities={identities.data ?? []}
|
|
532
|
+
setValue={(value) => setValue("identity", value)}
|
|
533
|
+
/>
|
|
454
534
|
</div>
|
|
455
535
|
</div>
|
|
456
536
|
</TabsContent>
|
|
@@ -461,6 +541,13 @@ export const Playground = ({
|
|
|
461
541
|
showPathParamsWarning={formState.pathParams.some(
|
|
462
542
|
(p) => p.value === "",
|
|
463
543
|
)}
|
|
544
|
+
showLongRunningWarning={showLongRunningWarning}
|
|
545
|
+
onCancel={() => {
|
|
546
|
+
abortControllerRef.current?.abort(
|
|
547
|
+
"Request cancelled by the user",
|
|
548
|
+
);
|
|
549
|
+
setShowLongRunningWarning(false);
|
|
550
|
+
}}
|
|
464
551
|
/>
|
|
465
552
|
</div>
|
|
466
553
|
</form>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Button } from "zudoku/ui/Button.js";
|
|
2
|
+
import {
|
|
3
|
+
Dialog,
|
|
4
|
+
DialogContent,
|
|
5
|
+
DialogDescription,
|
|
6
|
+
DialogFooter,
|
|
7
|
+
DialogTitle,
|
|
8
|
+
} from "zudoku/ui/Dialog.js";
|
|
9
|
+
|
|
10
|
+
const RequestLoginDialog = ({
|
|
11
|
+
open,
|
|
12
|
+
setOpen,
|
|
13
|
+
onSignUp,
|
|
14
|
+
onLogin,
|
|
15
|
+
}: {
|
|
16
|
+
open: boolean;
|
|
17
|
+
onSignUp?: () => void;
|
|
18
|
+
onLogin?: () => void;
|
|
19
|
+
setOpen: (open: boolean) => void;
|
|
20
|
+
}) => {
|
|
21
|
+
return (
|
|
22
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
23
|
+
<DialogContent>
|
|
24
|
+
<DialogTitle>Welcome to the Playground!</DialogTitle>
|
|
25
|
+
<DialogDescription>
|
|
26
|
+
The Playground is a tool for developers to test and explore our APIs.
|
|
27
|
+
To use the Playground, you need to login.
|
|
28
|
+
</DialogDescription>
|
|
29
|
+
<DialogFooter className="flex gap-2 sm:justify-between">
|
|
30
|
+
<Button type="button" variant="ghost" onClick={() => setOpen(false)}>
|
|
31
|
+
Skip
|
|
32
|
+
</Button>
|
|
33
|
+
<div className="flex gap-2">
|
|
34
|
+
{onSignUp && (
|
|
35
|
+
<Button type="button" variant="outline" onClick={onSignUp}>
|
|
36
|
+
Sign Up
|
|
37
|
+
</Button>
|
|
38
|
+
)}
|
|
39
|
+
{onLogin && (
|
|
40
|
+
<Button type="button" variant="default" onClick={onLogin}>
|
|
41
|
+
Login
|
|
42
|
+
</Button>
|
|
43
|
+
)}
|
|
44
|
+
</div>
|
|
45
|
+
</DialogFooter>
|
|
46
|
+
</DialogContent>
|
|
47
|
+
</Dialog>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default RequestLoginDialog;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { create } from "zustand";
|
|
2
|
+
import { createJSONStorage, persist } from "zustand/middleware";
|
|
3
|
+
|
|
4
|
+
interface IdentityState {
|
|
5
|
+
rememberedIdentity: string | null;
|
|
6
|
+
setRememberedIdentity: (identity: string | null) => void;
|
|
7
|
+
getRememberedIdentity: (availableIdentities: string[]) => string | undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const useIdentityStore = create<IdentityState>()(
|
|
11
|
+
persist(
|
|
12
|
+
(set, get) => ({
|
|
13
|
+
rememberedIdentity: null,
|
|
14
|
+
setRememberedIdentity: (identity: string | null) =>
|
|
15
|
+
set({ rememberedIdentity: identity }),
|
|
16
|
+
getRememberedIdentity: (availableIdentities: string[]) =>
|
|
17
|
+
availableIdentities.find(
|
|
18
|
+
(identity) => identity === get().rememberedIdentity,
|
|
19
|
+
),
|
|
20
|
+
}),
|
|
21
|
+
{
|
|
22
|
+
name: "identity-storage",
|
|
23
|
+
storage: createJSONStorage(() => sessionStorage),
|
|
24
|
+
},
|
|
25
|
+
),
|
|
26
|
+
);
|