zudoku 0.1.1-dev.44 → 0.1.1-dev.46

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 (32) hide show
  1. package/dist/lib/components/Select.d.ts +13 -0
  2. package/dist/lib/components/Select.js +27 -0
  3. package/dist/lib/components/Select.js.map +1 -0
  4. package/dist/lib/plugins/api-key/CreateApiKey.js +37 -0
  5. package/dist/lib/plugins/api-key/CreateApiKey.js.map +1 -0
  6. package/dist/lib/plugins/api-key/SettingsApiKeys.js +1 -1
  7. package/dist/lib/plugins/api-key/SettingsApiKeys.js.map +1 -1
  8. package/dist/lib/plugins/api-key/index.d.ts +1 -1
  9. package/dist/lib/plugins/api-key/index.js +1 -1
  10. package/dist/lib/plugins/api-key/index.js.map +1 -1
  11. package/dist/lib/plugins/openapi/OperationListItem.js +2 -22
  12. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  13. package/dist/lib/plugins/openapi/ParameterListItem.js +1 -1
  14. package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
  15. package/dist/lib/plugins/openapi/SchemaListView.d.ts +7 -0
  16. package/dist/lib/plugins/openapi/SchemaListView.js +43 -0
  17. package/dist/lib/plugins/openapi/SchemaListView.js.map +1 -0
  18. package/lib/{Spinner-Zry2k_pD.js → Spinner-DEkC7JSn.js} +2090 -1986
  19. package/lib/zudoku.components.js +507 -608
  20. package/lib/zudoku.plugins.js +15051 -12558
  21. package/package.json +3 -2
  22. package/src/app/main.css +1 -0
  23. package/src/lib/components/Select.tsx +157 -0
  24. package/src/lib/plugins/api-key/{CreateApiKeys.tsx → CreateApiKey.tsx} +43 -27
  25. package/src/lib/plugins/api-key/SettingsApiKeys.tsx +10 -9
  26. package/src/lib/plugins/api-key/index.tsx +2 -2
  27. package/src/lib/plugins/openapi/OperationListItem.tsx +7 -121
  28. package/src/lib/plugins/openapi/ParameterListItem.tsx +1 -1
  29. package/src/lib/plugins/openapi/SchemaListView.tsx +229 -0
  30. package/dist/lib/plugins/api-key/CreateApiKeys.js +0 -37
  31. package/dist/lib/plugins/api-key/CreateApiKeys.js.map +0 -1
  32. /package/dist/lib/plugins/api-key/{CreateApiKeys.d.ts → CreateApiKey.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.1.1-dev.44",
3
+ "version": "0.1.1-dev.46",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -53,6 +53,7 @@
53
53
  "@mdx-js/rollup": "3.0.1",
54
54
  "@monaco-editor/react": "^4.6.0",
55
55
  "@pothos/core": "3.41.0",
56
+ "@radix-ui/react-select": "2.1.1",
56
57
  "@sentry/node": "8.11.0",
57
58
  "@stefanprobst/rehype-extract-toc": "2.2.0",
58
59
  "@tailwindcss/typography": "0.5.13",
@@ -122,8 +123,8 @@
122
123
  "prism-react-renderer": "2.3.1",
123
124
  "prismjs": "1.29.0",
124
125
  "react-helmet-async": "2.0.5",
125
- "react-router-dom": "6.24.1",
126
126
  "react-markdown": "9.0.1",
127
+ "react-router-dom": "6.24.1",
127
128
  "tailwind-merge": "2.3.0",
128
129
  "typescript": "5.5.3",
129
130
  "zustand": "4.5.4"
package/src/app/main.css CHANGED
@@ -23,6 +23,7 @@
23
23
 
24
24
  html {
25
25
  scroll-padding-block: var(--scroll-padding);
26
+ scrollbar-gutter: stable;
26
27
  }
27
28
 
28
29
  body {
@@ -0,0 +1,157 @@
1
+ import * as React from "react";
2
+ import * as SelectPrimitive from "@radix-ui/react-select";
3
+ import { Check, ChevronDown, ChevronUp } from "lucide-react";
4
+ import { cn } from "../util/cn.js";
5
+
6
+ const Select = SelectPrimitive.Root;
7
+
8
+ const SelectGroup = SelectPrimitive.Group;
9
+
10
+ const SelectValue = SelectPrimitive.Value;
11
+
12
+ const SelectTrigger = React.forwardRef<
13
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
14
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
15
+ >(({ className, children, ...props }, ref) => (
16
+ <SelectPrimitive.Trigger
17
+ ref={ref}
18
+ className={cn(
19
+ "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
20
+ className,
21
+ )}
22
+ {...props}
23
+ >
24
+ {children}
25
+ <SelectPrimitive.Icon asChild>
26
+ <ChevronDown className="h-4 w-4 opacity-50" />
27
+ </SelectPrimitive.Icon>
28
+ </SelectPrimitive.Trigger>
29
+ ));
30
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
31
+
32
+ const SelectScrollUpButton = React.forwardRef<
33
+ React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
34
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
35
+ >(({ className, ...props }, ref) => (
36
+ <SelectPrimitive.ScrollUpButton
37
+ ref={ref}
38
+ className={cn(
39
+ "flex cursor-default items-center justify-center py-1",
40
+ className,
41
+ )}
42
+ {...props}
43
+ >
44
+ <ChevronUp className="h-4 w-4" />
45
+ </SelectPrimitive.ScrollUpButton>
46
+ ));
47
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
48
+
49
+ const SelectScrollDownButton = React.forwardRef<
50
+ React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
51
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
52
+ >(({ className, ...props }, ref) => (
53
+ <SelectPrimitive.ScrollDownButton
54
+ ref={ref}
55
+ className={cn(
56
+ "flex cursor-default items-center justify-center py-1",
57
+ className,
58
+ )}
59
+ {...props}
60
+ >
61
+ <ChevronDown className="h-4 w-4" />
62
+ </SelectPrimitive.ScrollDownButton>
63
+ ));
64
+ SelectScrollDownButton.displayName =
65
+ SelectPrimitive.ScrollDownButton.displayName;
66
+
67
+ const SelectContent = React.forwardRef<
68
+ React.ElementRef<typeof SelectPrimitive.Content>,
69
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
70
+ >(({ className, children, position = "popper", ...props }, ref) => (
71
+ <SelectPrimitive.Portal>
72
+ <SelectPrimitive.Content
73
+ ref={ref}
74
+ className={cn(
75
+ "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
76
+ position === "popper" &&
77
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
78
+ className,
79
+ )}
80
+ position={position}
81
+ {...props}
82
+ >
83
+ <SelectScrollUpButton />
84
+ <SelectPrimitive.Viewport
85
+ className={cn(
86
+ "p-1",
87
+ position === "popper" &&
88
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
89
+ )}
90
+ >
91
+ {children}
92
+ </SelectPrimitive.Viewport>
93
+ <SelectScrollDownButton />
94
+ </SelectPrimitive.Content>
95
+ </SelectPrimitive.Portal>
96
+ ));
97
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
98
+
99
+ const SelectLabel = React.forwardRef<
100
+ React.ElementRef<typeof SelectPrimitive.Label>,
101
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
102
+ >(({ className, ...props }, ref) => (
103
+ <SelectPrimitive.Label
104
+ ref={ref}
105
+ className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
106
+ {...props}
107
+ />
108
+ ));
109
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
110
+
111
+ const SelectItem = React.forwardRef<
112
+ React.ElementRef<typeof SelectPrimitive.Item>,
113
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
114
+ >(({ className, children, ...props }, ref) => (
115
+ <SelectPrimitive.Item
116
+ ref={ref}
117
+ className={cn(
118
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
119
+ className,
120
+ )}
121
+ {...props}
122
+ >
123
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
124
+ <SelectPrimitive.ItemIndicator>
125
+ <Check className="h-4 w-4" />
126
+ </SelectPrimitive.ItemIndicator>
127
+ </span>
128
+
129
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
130
+ </SelectPrimitive.Item>
131
+ ));
132
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
133
+
134
+ const SelectSeparator = React.forwardRef<
135
+ React.ElementRef<typeof SelectPrimitive.Separator>,
136
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
137
+ >(({ className, ...props }, ref) => (
138
+ <SelectPrimitive.Separator
139
+ ref={ref}
140
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
141
+ {...props}
142
+ />
143
+ ));
144
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
145
+
146
+ export {
147
+ Select,
148
+ SelectGroup,
149
+ SelectValue,
150
+ SelectTrigger,
151
+ SelectContent,
152
+ SelectLabel,
153
+ SelectItem,
154
+ SelectSeparator,
155
+ SelectScrollUpButton,
156
+ SelectScrollDownButton,
157
+ };
@@ -3,11 +3,18 @@ import { type ApiKeyPluginOptions, ApiKeyService } from "./index.js";
3
3
  import { useMutation } from "@tanstack/react-query";
4
4
  import { Button } from "../../ui/Button.js";
5
5
  import { Input } from "../../components/Input.js";
6
- import { cn } from "../../util/cn.js";
7
6
  import { Link, useNavigate } from "react-router-dom";
8
7
  import { useForm } from "react-hook-form";
8
+ import {
9
+ Select,
10
+ SelectContent,
11
+ SelectGroup,
12
+ SelectItem,
13
+ SelectTrigger,
14
+ SelectValue,
15
+ } from "../../components/Select.js";
9
16
 
10
- type CreateApiKeys = { description: string; expiresAt?: string };
17
+ type CreateApiKey = { description: string; expiresOn?: string };
11
18
 
12
19
  export const CreateApiKey = ({
13
20
  options,
@@ -18,14 +25,24 @@ export const CreateApiKey = ({
18
25
  }) => {
19
26
  const context = useDevPortal();
20
27
  const navigate = useNavigate();
21
- const form = useForm<CreateApiKeys>();
28
+ const form = useForm<CreateApiKey>({
29
+ defaultValues: {
30
+ expiresOn: "30",
31
+ },
32
+ });
22
33
  const createKeyMutation = useMutation({
23
- mutationFn: ({ description, expiresAt }: CreateApiKeys) => {
34
+ mutationFn: ({ description, expiresOn }: CreateApiKey) => {
24
35
  if (!service.createKey) {
25
36
  throw new Error("deleteKey not implemented");
26
37
  }
27
38
 
28
- return service.createKey({ description }, context);
39
+ const expiresOnDate =
40
+ expiresOn !== "never" ? addDaysToDate(Number(expiresOn)) : undefined;
41
+
42
+ return service.createKey(
43
+ { description: description, expiresOn: expiresOnDate },
44
+ context,
45
+ );
29
46
  },
30
47
  onSuccess: () => navigate("/settings/api-keys/"),
31
48
  });
@@ -36,35 +53,34 @@ export const CreateApiKey = ({
36
53
 
37
54
  return (
38
55
  <div className="max-w-screen-lg pt-[--padding-content-top] pb-[--padding-content-bottom]">
39
- <div className="flex justify-between mb-4 border-b pb-1">
40
- <h1 className="font-medium text-2xl">New API Keys</h1>
56
+ <div className="flex justify-between mb-4 border-b border-border pb-1">
57
+ <h1 className="font-medium text-2xl">New API Key</h1>
41
58
  </div>
42
59
  <form
43
- onSubmit={form.handleSubmit((data) => {
44
- if (data.expiresAt === "never") {
45
- delete data.expiresAt;
46
- }
47
- createKeyMutation.mutate(data);
48
- })}
60
+ onSubmit={form.handleSubmit((data) => createKeyMutation.mutate(data))}
49
61
  >
50
62
  <div className="flex gap-2 flex-col">
51
63
  Note
52
64
  <Input {...form.register("description")} />
53
65
  Expiration
54
- <select
55
- className={cn(
56
- "row-start-1 col-start-1 border border-input text-foreground px-2 py-1 pe-6",
57
- "rounded-md appearance-none bg-zinc-50 hover:bg-white dark:bg-zinc-800 hover:dark:bg-zinc-800/75",
58
- )}
59
- {...form.register("expiresAt")}
66
+ <Select
67
+ onValueChange={(value) => form.setValue("expiresOn", value)}
68
+ defaultValue={form.getValues("expiresOn")}
60
69
  >
61
- <option value="never">Never</option>
62
- {[7, 30, 60, 90].map((option) => (
63
- <option value={addDaysToDate(option)} key={option}>
64
- {option} Days
65
- </option>
66
- ))}
67
- </select>
70
+ <SelectTrigger>
71
+ <SelectValue />
72
+ </SelectTrigger>
73
+ <SelectContent>
74
+ <SelectGroup>
75
+ {[7, 30, 60, 90].map((option) => (
76
+ <SelectItem value={String(option)} key={option}>
77
+ {option} days
78
+ </SelectItem>
79
+ ))}
80
+ <SelectItem value="never">Never</SelectItem>
81
+ </SelectGroup>
82
+ </SelectContent>
83
+ </Select>
68
84
  <div className="flex gap-2">
69
85
  <Button>Generate Key</Button>
70
86
  <Button variant="outline" asChild>
@@ -80,5 +96,5 @@ export const CreateApiKey = ({
80
96
  const addDaysToDate = (days: number): string => {
81
97
  const date = new Date();
82
98
  date.setDate(date.getDate() + days);
83
- return date.toISOString().split("T")[0];
99
+ return date.toISOString();
84
100
  };
@@ -40,7 +40,7 @@ export const SettingsApiKeys = ({
40
40
 
41
41
  return (
42
42
  <div className="max-w-screen-lg pt-[--padding-content-top] pb-[--padding-content-bottom]">
43
- <div className="flex justify-between mb-4 border-b border-border pb-1">
43
+ <div className="flex justify-between mb-4 border-b border-border pb-3">
44
44
  <h1 className="font-medium text-2xl">API Keys</h1>
45
45
  {service.createKey && (
46
46
  <Button asChild>
@@ -58,14 +58,15 @@ export const SettingsApiKeys = ({
58
58
  >
59
59
  <div className="flex flex-col gap-1 text-sm">
60
60
  {key.description ?? key.id}
61
- {key.expiresOn ? (
62
- <div>Expires on {key.expiresOn}</div>
63
- ) : (
64
- key.createdOn && (
65
- <div className="text-gray-500 whitespace-pre">
66
- Created on {new Date(key.createdOn).toLocaleDateString()}
67
- </div>
68
- )
61
+ {key.createdOn && (
62
+ <div className="text-muted-foreground">
63
+ Created on {new Date(key.createdOn).toLocaleString()}
64
+ </div>
65
+ )}
66
+ {key.expiresOn && (
67
+ <div className="text-muted-foreground">
68
+ Expires on {new Date(key.expiresOn).toLocaleString()}
69
+ </div>
69
70
  )}
70
71
  </div>
71
72
  <div className="items-center flex justify-center">
@@ -4,7 +4,7 @@ import {
4
4
  type DevPortalPlugin,
5
5
  } from "../../core/plugins.js";
6
6
  import { SettingsApiKeys } from "./SettingsApiKeys.js";
7
- import { CreateApiKey } from "./CreateApiKeys.js";
7
+ import { CreateApiKey } from "./CreateApiKey.js";
8
8
 
9
9
  export type ApiKeyResults = Promise<ApiKey[]>;
10
10
  const DEFAULT_API_KEY_ENDPOINT =
@@ -20,7 +20,7 @@ export type ApiKeyService = {
20
20
  ) => Promise<void>;
21
21
  getUsage?: (apiKeys: string[], context: DevPortalContext) => Promise<void>;
22
22
  createKey?: (
23
- apiKey: { description: string; expiresAt?: string },
23
+ apiKey: { description: string; expiresOn?: string },
24
24
  context: DevPortalContext,
25
25
  ) => Promise<void>;
26
26
  };
@@ -5,10 +5,8 @@ import { OperationsFragment } from "./OperationList.js";
5
5
  import { ParameterList } from "./ParameterList.js";
6
6
  import { Sidecar } from "./Sidecar.js";
7
7
  import { FragmentType, useFragment } from "./graphql/index.js";
8
- import { SchemaObject } from "../../oas/parser/index.js";
9
- import { useState } from "react";
10
- import { cn } from "../../util/cn.js";
11
8
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../ui/Tabs.js";
9
+ import { SchemaListView } from "./SchemaListView.js";
12
10
 
13
11
  export const PARAM_GROUPS = ["path", "query", "header", "cookie"] as const;
14
12
  export type ParameterGroup = (typeof PARAM_GROUPS)[number];
@@ -61,8 +59,9 @@ export const OperationListItem = ({
61
59
  <TabsTrigger
62
60
  value={response.statusCode + response.description}
63
61
  key={response.statusCode}
62
+ title={response.description}
64
63
  >
65
- {response.description} ({response.statusCode})
64
+ {response.statusCode}
66
65
  </TabsTrigger>
67
66
  ))}
68
67
  </TabsList>
@@ -72,7 +71,10 @@ export const OperationListItem = ({
72
71
  value={response.statusCode + response.description}
73
72
  key={response.statusCode}
74
73
  >
75
- <ViewSchema schema={response.content?.at(0)?.schema} name="" />
74
+ <SchemaListView
75
+ schema={response.content?.at(0)?.schema}
76
+ name=""
77
+ />
76
78
  </TabsContent>
77
79
  ))}
78
80
  </ul>
@@ -83,119 +85,3 @@ export const OperationListItem = ({
83
85
  </div>
84
86
  );
85
87
  };
86
-
87
- const ViewSchema = ({
88
- name,
89
- schema,
90
- level = 0,
91
- collapsible = false,
92
- }: {
93
- level?: number;
94
- collapsible?: boolean;
95
- name?: string;
96
- schema: SchemaObject;
97
- }) => {
98
- const [open, setOpen] = useState(!collapsible);
99
-
100
- const properties = Object.entries(schema.properties ?? {});
101
- const additionalProperties =
102
- typeof schema.additionalProperties === "object"
103
- ? Object.entries(schema.additionalProperties)
104
- : [];
105
-
106
- return (
107
- <div
108
- className={cn(
109
- "not-prose",
110
- level > 0 && "border border-border rounded text-sm",
111
- )}
112
- onClick={
113
- collapsible
114
- ? () => {
115
- setOpen((open) => !open);
116
- }
117
- : undefined
118
- }
119
- >
120
- {(schema.title ?? name) && (
121
- <div className="ml-2 my-1 font-bold">{schema.title ?? name}</div>
122
- )}
123
- {level === 0 && <p>schema.description</p>}
124
- <ul>
125
- {open &&
126
- properties
127
- .concat(additionalProperties)
128
- .map(([propertyName, property]) => (
129
- <div
130
- key={propertyName}
131
- className={cn(
132
- level > 0 ? "py-2" : "py-4",
133
- "px-2 border-t border-border bg-border/20 hover:bg-border/30 flex gap-1 flex-col",
134
- property.deprecated && "opacity-50",
135
- )}
136
- >
137
- <div className="flex items-center gap-2 relative">
138
- <code>
139
- {propertyName} {property.title}
140
- </code>
141
-
142
- {property.type && (
143
- <span className="text-sm text-muted-foreground">
144
- {property.type}
145
- </span>
146
- )}
147
- {property.deprecated && (
148
- <span className="text-sm text-muted-foreground">
149
- Deprecated
150
- </span>
151
- )}
152
-
153
- {!schema.required?.includes(propertyName) &&
154
- !property.required && (
155
- <span className="py-px px-1.5 font-medium text-xs border border-border rounded-lg">
156
- optional
157
- </span>
158
- )}
159
- {/*{property.type === "object" && (*/}
160
- {/* <div className="absolute right-3">+</div>*/}
161
- {/*)}*/}
162
- </div>
163
- {property.description && (
164
- <Markdown
165
- content={property.description}
166
- className="prose text-sm prose-p:my-1 leading-normal line-clamp-4"
167
- />
168
- )}
169
-
170
- {property.enum && (
171
- <span className="text-sm text-muted-foreground flex gap-1 flex-wrap items-center">
172
- <span>Possible values</span>
173
- {property.enum
174
- .filter((value) => value)
175
- .map((value) => (
176
- <span
177
- key={value}
178
- className="font-mono text-xs border-border border bg-muted rounded px-1"
179
- >
180
- {value}
181
- </span>
182
- ))}
183
- </span>
184
- )}
185
- {property.type === "object" && (
186
- <div className="mt-2.5">
187
- <ViewSchema schema={property} level={level + 1} />
188
- </div>
189
- )}
190
- {property.type === "array" &&
191
- property.items.type === "object" && (
192
- <div className="mt-2.5">
193
- <ViewSchema schema={property.items} level={level + 1} />
194
- </div>
195
- )}
196
- </div>
197
- ))}
198
- </ul>
199
- </div>
200
- );
201
- };
@@ -28,7 +28,7 @@ export const ParameterListItem = ({
28
28
  group: ParameterGroup;
29
29
  id: string;
30
30
  }) => (
31
- <li className="not-prose px-2 py-4 border-t border-border bg-border/20">
31
+ <li className="not-prose px-2 py-4 border-t border-border bg-border/20 text-sm flex flex-col gap-1">
32
32
  <div className="flex items-center gap-2">
33
33
  <code>
34
34
  {group === "path" ? (