zudoku 0.1.1-dev.11 → 0.1.1-dev.13

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 (104) hide show
  1. package/dist/app/DevPortal.js +2 -15
  2. package/dist/app/DevPortal.js.map +1 -1
  3. package/dist/app/Router.d.ts +4 -0
  4. package/dist/app/Router.js +20 -0
  5. package/dist/app/Router.js.map +1 -0
  6. package/dist/vite/config.d.ts +2 -2
  7. package/dist/vite/config.js +18 -8
  8. package/dist/vite/config.js.map +1 -1
  9. package/dist/vite/plugin-api.js +1 -1
  10. package/dist/vite/plugin-api.js.map +1 -1
  11. package/dist/vite/plugin-auth.js +1 -1
  12. package/dist/vite/plugin-docs.js +1 -1
  13. package/dist/vite/plugin-docs.js.map +1 -1
  14. package/package.json +4 -3
  15. package/src/app/App.tsx +0 -30
  16. package/src/app/DevPortal.tsx +0 -115
  17. package/src/app/Heading.tsx +0 -60
  18. package/src/app/authentication/authentication.ts +0 -18
  19. package/src/app/authentication/clerk.ts +0 -47
  20. package/src/app/authentication/openid.ts +0 -192
  21. package/src/app/components/AnchorLink.tsx +0 -19
  22. package/src/app/components/CategoryHeading.tsx +0 -16
  23. package/src/app/components/Dialog.tsx +0 -119
  24. package/src/app/components/DynamicIcon.tsx +0 -60
  25. package/src/app/components/Header.tsx +0 -69
  26. package/src/app/components/Input.tsx +0 -24
  27. package/src/app/components/Layout.tsx +0 -56
  28. package/src/app/components/Markdown.tsx +0 -37
  29. package/src/app/components/SyntaxHighlight.tsx +0 -94
  30. package/src/app/components/TopNavigation.tsx +0 -32
  31. package/src/app/components/context/ComponentsContext.tsx +0 -24
  32. package/src/app/components/context/DevPortalProvider.ts +0 -54
  33. package/src/app/components/context/PluginSystem.ts +0 -0
  34. package/src/app/components/context/ThemeContext.tsx +0 -46
  35. package/src/app/components/context/ViewportAnchorContext.tsx +0 -139
  36. package/src/app/components/navigation/SideNavigation.tsx +0 -18
  37. package/src/app/components/navigation/SideNavigationCategory.tsx +0 -74
  38. package/src/app/components/navigation/SideNavigationItem.tsx +0 -143
  39. package/src/app/components/navigation/SideNavigationWrapper.tsx +0 -15
  40. package/src/app/components/navigation/useNavigationCollapsibleState.ts +0 -27
  41. package/src/app/components/navigation/util.ts +0 -38
  42. package/src/app/core/DevPortalContext.ts +0 -164
  43. package/src/app/core/helmet.ts +0 -5
  44. package/src/app/core/icons.tsx +0 -1
  45. package/src/app/core/plugins.ts +0 -43
  46. package/src/app/core/router.tsx +0 -1
  47. package/src/app/core/types/combine.ts +0 -16
  48. package/src/app/main.tsx +0 -9
  49. package/src/app/oas/graphql/index.ts +0 -422
  50. package/src/app/oas/graphql/server.ts +0 -10
  51. package/src/app/oas/parser/dereference/index.ts +0 -59
  52. package/src/app/oas/parser/dereference/resolveRef.ts +0 -32
  53. package/src/app/oas/parser/index.ts +0 -94
  54. package/src/app/oas/parser/schemas/v3.0.json +0 -1489
  55. package/src/app/oas/parser/schemas/v3.1.json +0 -1298
  56. package/src/app/oas/parser/upgrade/index.ts +0 -108
  57. package/src/app/plugins/api-key/SettingsApiKeys.tsx +0 -22
  58. package/src/app/plugins/api-key/index.tsx +0 -123
  59. package/src/app/plugins/markdown/MdxPage.tsx +0 -128
  60. package/src/app/plugins/markdown/Toc.tsx +0 -122
  61. package/src/app/plugins/markdown/generateRoutes.tsx +0 -72
  62. package/src/app/plugins/markdown/index.tsx +0 -31
  63. package/src/app/plugins/openapi/ColorizedParam.tsx +0 -82
  64. package/src/app/plugins/openapi/MakeRequest.tsx +0 -49
  65. package/src/app/plugins/openapi/MethodBadge.tsx +0 -36
  66. package/src/app/plugins/openapi/OperationList.tsx +0 -117
  67. package/src/app/plugins/openapi/OperationListItem.tsx +0 -55
  68. package/src/app/plugins/openapi/ParameterList.tsx +0 -32
  69. package/src/app/plugins/openapi/ParameterListItem.tsx +0 -60
  70. package/src/app/plugins/openapi/RequestBodySidecarBox.tsx +0 -51
  71. package/src/app/plugins/openapi/ResponsesSidecarBox.tsx +0 -60
  72. package/src/app/plugins/openapi/Select.tsx +0 -35
  73. package/src/app/plugins/openapi/Sidecar.tsx +0 -160
  74. package/src/app/plugins/openapi/SidecarBox.tsx +0 -36
  75. package/src/app/plugins/openapi/graphql/fragment-masking.ts +0 -111
  76. package/src/app/plugins/openapi/graphql/gql.ts +0 -70
  77. package/src/app/plugins/openapi/graphql/graphql.ts +0 -795
  78. package/src/app/plugins/openapi/graphql/index.ts +0 -2
  79. package/src/app/plugins/openapi/index.tsx +0 -142
  80. package/src/app/plugins/openapi/playground/Playground.tsx +0 -309
  81. package/src/app/plugins/openapi/queries.graphql +0 -6
  82. package/src/app/plugins/openapi/util/generateSchemaExample.ts +0 -59
  83. package/src/app/plugins/openapi/util/urql.ts +0 -8
  84. package/src/app/plugins/openapi/worker/createSharedWorkerClient.ts +0 -60
  85. package/src/app/plugins/openapi/worker/worker.ts +0 -30
  86. package/src/app/plugins/redirect/index.tsx +0 -20
  87. package/src/app/tailwind.ts +0 -72
  88. package/src/app/ui/Button.tsx +0 -56
  89. package/src/app/ui/Callout.tsx +0 -87
  90. package/src/app/ui/Card.tsx +0 -82
  91. package/src/app/ui/Note.tsx +0 -58
  92. package/src/app/ui/Tabs.tsx +0 -52
  93. package/src/app/util/MdxComponents.tsx +0 -70
  94. package/src/app/util/cn.ts +0 -6
  95. package/src/app/util/createVariantComponent.tsx +0 -30
  96. package/src/app/util/createWaitForNotify.ts +0 -18
  97. package/src/app/util/groupBy.ts +0 -24
  98. package/src/app/util/joinPath.tsx +0 -10
  99. package/src/app/util/pastellize.ts +0 -25
  100. package/src/app/util/slugify.ts +0 -3
  101. package/src/app/util/traverseNavigation.ts +0 -55
  102. package/src/app/util/useScrollToAnchor.ts +0 -38
  103. package/src/app/util/useScrollToTop.ts +0 -13
  104. /package/{src → dist}/app/main.css +0 -0
@@ -1,164 +0,0 @@
1
- import { QueryClient } from "@tanstack/react-query";
2
- import { type ReactNode } from "react";
3
- import { create } from "zustand";
4
- import { type DevPortalPath, type DevPortalProps } from "../DevPortal.js";
5
- import { type AuthProvider } from "../authentication/authentication.js";
6
- import {
7
- isApiKeyPlugin,
8
- isNavigationPlugin,
9
- needsInitialization,
10
- type DevPortalPlugin,
11
- type NavigationPlugin,
12
- } from "./plugins.js";
13
-
14
- import type { LucideIcon } from "lucide-react";
15
- import type { IconName } from "../components/DynamicIcon.js";
16
-
17
- export interface ApiIdentity {
18
- authorizeRequest: (request: Request) => Request;
19
- name: string;
20
- id: string;
21
- }
22
-
23
- type BaseNavigationCategoryItem = {
24
- label: ReactNode;
25
- title?: string;
26
- icon?: ReactNode | IconName;
27
- };
28
-
29
- export type PathNavigationCategoryItem = BaseNavigationCategoryItem & {
30
- path: string;
31
- children?: NavigationCategoryItem[];
32
- };
33
-
34
- export type HrefNavigationCategoryItem = BaseNavigationCategoryItem & {
35
- href: string;
36
- };
37
-
38
- export type NavigationCategoryItem =
39
- | PathNavigationCategoryItem
40
- | HrefNavigationCategoryItem;
41
-
42
- export type NavigationCategory = {
43
- label: string;
44
- path?: string;
45
- expanded?: boolean;
46
- collapsible?: boolean;
47
- icon?: LucideIcon;
48
- children: NavigationCategoryItem[];
49
- };
50
-
51
- export type NavigationItem = {
52
- label: string;
53
- path: DevPortalPath;
54
- categories?: NavigationCategory[];
55
- };
56
-
57
- export type SessionProfileState = {
58
- isLoggedIn: boolean;
59
- sub?: string;
60
- email?: string;
61
- name?: string;
62
- email_verified?: string;
63
- picture?: string;
64
- };
65
-
66
- export type RoutingState = {
67
- path?: string;
68
- };
69
-
70
- export const queryClient = new QueryClient();
71
-
72
- export const useDevPortalState = create<SessionProfileState & RoutingState>(
73
- () => ({
74
- isLoggedIn: false,
75
- }),
76
- );
77
-
78
- export type ApiKeyCache = "api-keys";
79
- export type DevPortalCacheKey = ApiKeyCache | string;
80
-
81
- export class DevPortalContext {
82
- private plugins: DevPortalPlugin[] = [];
83
- private navigationPlugins: NavigationPlugin[];
84
-
85
- public navigation: NavigationItem[];
86
- public meta: DevPortalProps["meta"];
87
- public authentication?: AuthProvider;
88
- public state: typeof useDevPortalState;
89
-
90
- constructor(private config: DevPortalProps) {
91
- this.plugins = config.plugins ?? [];
92
- this.navigation = config.navigation;
93
- this.navigationPlugins = this.plugins.filter(isNavigationPlugin);
94
- this.authentication = config.authentication;
95
- this.meta = config.meta;
96
- this.state = useDevPortalState;
97
- }
98
-
99
- initialize = async () => {
100
- this.plugins
101
- .filter(needsInitialization)
102
- .forEach((plugin) => plugin.initialize(this));
103
- this.authentication?.initialize(this);
104
- };
105
-
106
- setUserProfile = async (profile: Partial<SessionProfileState>) => {
107
- return useDevPortalState.setState(profile);
108
- };
109
-
110
- invalidateCache = async (key: DevPortalCacheKey[]) => {
111
- queryClient.invalidateQueries({ queryKey: key });
112
- };
113
-
114
- getApiIdentities = async () => {
115
- const keys = await Promise.all(
116
- this.plugins
117
- .filter(isApiKeyPlugin)
118
- .map((plugin) => plugin.getIdentities(this)),
119
- );
120
-
121
- return keys.flat();
122
- };
123
-
124
- login = async () => {
125
- if (!this.authentication) {
126
- throw new Error("No authentication configured");
127
- }
128
-
129
- return this.authentication.login(this);
130
- };
131
-
132
- logout = async () => {
133
- if (!this.authentication) {
134
- throw new Error("No authentication configured");
135
- }
136
-
137
- return this.authentication.signOut(this);
138
- };
139
-
140
- handleAuthenticationResponse = async (path: {
141
- pathname: string;
142
- search: string;
143
- hash: string;
144
- }) => {
145
- this.config.authentication?.handleAuthenticationResponse?.(path, this);
146
- };
147
-
148
- getNavigation = async (path: string) => {
149
- const navigations = await Promise.all(
150
- this.navigationPlugins.map(async (plugin) =>
151
- plugin.getNavigation?.(path),
152
- ),
153
- );
154
-
155
- return navigations.flatMap((nav) => nav ?? []);
156
- };
157
-
158
- sessionStorage = {
159
- get: async (key: string) => localStorage.getItem(`session-${key}`) ?? "",
160
- setProfile: async () => {},
161
- set: async (key: string, value: string) =>
162
- localStorage.setItem(`session-${key}`, value),
163
- };
164
- }
@@ -1,5 +0,0 @@
1
- import * as helmet from "react-helmet-async";
2
-
3
- export const Helmet = helmet.Helmet;
4
- export const HelmetData = helmet.HelmetData;
5
- export const HelmetProvider = helmet.HelmetProvider;
@@ -1 +0,0 @@
1
- export * from "lucide-react";
@@ -1,43 +0,0 @@
1
- import { type RouteObject } from "react-router-dom";
2
- import {
3
- DevPortalContext,
4
- type ApiIdentity,
5
- type NavigationCategory,
6
- } from "./DevPortalContext.js";
7
- import { type Combine } from "./types/combine.js";
8
-
9
- export type PluginNavigationCategory = {
10
- path: string;
11
- } & NavigationCategory;
12
-
13
- export type DevPortalPlugin = Combine<
14
- [NavigationPlugin, ApiIdentityPlugin, InitializationPlugin]
15
- >;
16
-
17
- export interface NavigationPlugin {
18
- getRoutes: () => RouteObject[];
19
- getNavigation?: (path: string) => Promise<PluginNavigationCategory[]>;
20
- }
21
-
22
- export interface ApiIdentityPlugin {
23
- getIdentities: (context: DevPortalContext) => Promise<ApiIdentity[]>;
24
- }
25
-
26
- export interface InitializationPlugin {
27
- initialize: (context: DevPortalContext) => Promise<void> | void;
28
- }
29
-
30
- export const isNavigationPlugin = (
31
- obj: DevPortalPlugin,
32
- ): obj is NavigationPlugin =>
33
- "getRoutes" in obj && typeof obj.getRoutes === "function";
34
-
35
- export const needsInitialization = (
36
- obj: DevPortalPlugin,
37
- ): obj is InitializationPlugin =>
38
- "initialize" in obj && typeof obj.initialize === "function";
39
-
40
- export const isApiKeyPlugin = (
41
- obj: DevPortalPlugin,
42
- ): obj is ApiIdentityPlugin =>
43
- "getIdentities" in obj && typeof obj.getIdentities === "function";
@@ -1 +0,0 @@
1
- export * from "react-router-dom";
@@ -1,16 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
3
- k: infer I,
4
- ) => void
5
- ? I
6
- : never;
7
-
8
- type AnyCombination<T, U = T> = T extends any
9
- ? U extends any
10
- ? T | (T & U)
11
- : never
12
- : never;
13
-
14
- export type Combine<T extends any[]> =
15
- | UnionToIntersection<T[number]>
16
- | AnyCombination<T[number]>;
package/src/app/main.tsx DELETED
@@ -1,9 +0,0 @@
1
- import React from "react";
2
- import { createRoot } from "react-dom/client";
3
- import App from "./App.js";
4
-
5
- createRoot(document.getElementById("root")!).render(
6
- <React.StrictMode>
7
- <App />
8
- </React.StrictMode>,
9
- );
@@ -1,422 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import SchemaBuilder from "@pothos/core";
3
- import { GraphQLJSON, GraphQLJSONObject } from "graphql-type-json";
4
- import { createYoga, type YogaServerOptions } from "graphql-yoga";
5
- import { LRUCache } from "lru-cache";
6
- import hashit from "object-hash";
7
- import slugify from "../../util/slugify.js";
8
- import {
9
- HttpMethods,
10
- validate,
11
- type EncodingObject,
12
- type ExampleObject,
13
- type OpenAPIDocument,
14
- type OperationObject,
15
- type ParameterObject,
16
- type PathsObject,
17
- type SchemaObject,
18
- type TagObject,
19
- } from "../parser/index.js";
20
-
21
- export type {
22
- EncodingObject,
23
- ExampleObject,
24
- OpenAPIDocument,
25
- OperationObject,
26
- ParameterObject,
27
- PathsObject,
28
- SchemaObject,
29
- TagObject,
30
- };
31
-
32
- type OperationLike = {
33
- summary?: string | null;
34
- operationId?: string | null;
35
- path: string;
36
- method: string;
37
- };
38
- export const slugifyOperation = (operation: OperationLike, tag?: string) => {
39
- return slugify(
40
- (tag ? tag + "-" : "") +
41
- (operation.summary ??
42
- operation.operationId ??
43
- `${operation.method}-${operation.path}`),
44
- { lower: true, trim: true },
45
- );
46
- };
47
-
48
- const cache = new LRUCache<string, OpenAPIDocument>({
49
- ttl: 60 * 10 * 1000,
50
- ttlAutopurge: true,
51
- });
52
-
53
- const builder = new SchemaBuilder<{
54
- Scalars: {
55
- JSON: any;
56
- JSONObject: any;
57
- };
58
- Context: {
59
- schema: OpenAPIDocument;
60
- };
61
- }>({});
62
-
63
- const JSONScalar = builder.addScalarType("JSON", GraphQLJSON);
64
- const JSONObjectScalar = builder.addScalarType("JSONObject", GraphQLJSONObject);
65
-
66
- const getAllOperations = (paths?: PathsObject, tag?: string) => {
67
- return Object.entries(paths ?? {}).flatMap(([path, value]) =>
68
- HttpMethods.flatMap((method) => {
69
- if (!value?.[method]) return [];
70
-
71
- const operation = value[method] as OperationObject;
72
- const pathParameters = value?.parameters ?? [];
73
- const operationParameters = operation.parameters ?? [];
74
-
75
- // parameters are inherited from the parent path object,
76
- // but can be overridden by their `name` and `in` location
77
- const parameters = [
78
- ...pathParameters.filter(
79
- // remove path parameters that are already defined in the operation
80
- (pp) =>
81
- !operationParameters.some(
82
- (op) => op.name === pp.name && op.in === pp.in,
83
- ),
84
- ),
85
- ...operationParameters,
86
- ];
87
-
88
- return {
89
- ...operation,
90
- method,
91
- path,
92
- parameters,
93
- slug: slugifyOperation(
94
- {
95
- summary: operation.summary,
96
- operationId: operation.operationId,
97
- path,
98
- method,
99
- },
100
- tag,
101
- ),
102
- };
103
- }),
104
- );
105
- };
106
-
107
- const SchemaTag = builder.objectRef<TagObject>("SchemaTag").implement({
108
- fields: (t) => ({
109
- name: t.exposeString("name", { nullable: true }),
110
- description: t.exposeString("description", { nullable: true }),
111
- operations: t.field({
112
- type: [OperationItem],
113
- resolve: (parent, _args, ctx) =>
114
- getAllOperations(ctx.schema.paths, parent.name).filter((item) =>
115
- parent.name
116
- ? item.tags?.includes(parent.name)
117
- : !item.tags || item.tags.length === 0,
118
- ),
119
- }),
120
- }),
121
- });
122
-
123
- const PathItem = builder
124
- .objectRef<{
125
- path: string;
126
- methods: typeof HttpMethods;
127
- }>("PathItem")
128
- .implement({
129
- fields: (t) => ({
130
- path: t.exposeString("path"),
131
- methods: t.exposeStringList("methods"),
132
- }),
133
- });
134
-
135
- const TagItem = builder.objectRef<TagObject>("TagItem").implement({
136
- fields: (t) => ({
137
- name: t.exposeString("name"),
138
- description: t.exposeString("description", { nullable: true }),
139
- }),
140
- });
141
-
142
- const EncodingItem = builder
143
- .objectRef<EncodingObject & { name: string }>("EncodingItem")
144
- .implement({
145
- fields: (t) => ({
146
- name: t.exposeString("name"),
147
- contentType: t.exposeString("contentType", { nullable: true }),
148
- headers: t.expose("headers", { type: JSONObjectScalar, nullable: true }),
149
- style: t.exposeString("style", { nullable: true }),
150
- explode: t.exposeBoolean("explode", { nullable: true }),
151
- allowReserved: t.exposeBoolean("allowReserved", { nullable: true }),
152
- }),
153
- });
154
-
155
- const ExampleItem = builder
156
- .objectRef<ExampleObject & { name: string }>("ExampleItem")
157
- .implement({
158
- fields: (t) => ({
159
- name: t.exposeString("name"),
160
- summary: t.exposeString("summary", { nullable: true }),
161
- description: t.exposeString("description", { nullable: true }),
162
- value: t.exposeString("value", { nullable: true }),
163
- externalValue: t.exposeString("externalValue", { nullable: true }),
164
- }),
165
- });
166
-
167
- const ParameterIn = builder.enumType("ParameterIn", {
168
- values: ["query", "header", "path", "cookie"] as const,
169
- });
170
-
171
- const ParameterItem = builder
172
- .objectRef<ParameterObject>("ParameterItem")
173
- .implement({
174
- fields: (t) => ({
175
- name: t.exposeString("name"),
176
- in: t.field({
177
- type: ParameterIn,
178
- resolve: (parent) => parent.in as typeof ParameterIn.$inferType,
179
- }),
180
- description: t.exposeString("description", { nullable: true }),
181
- required: t.exposeBoolean("required", { nullable: true }),
182
- deprecated: t.exposeBoolean("deprecated", { nullable: true }),
183
- allowEmptyValue: t.exposeBoolean("allowEmptyValue", { nullable: true }),
184
- style: t.exposeString("style", { nullable: true }),
185
- explode: t.exposeBoolean("explode", { nullable: true }),
186
- allowReserved: t.exposeBoolean("allowReserved", { nullable: true }),
187
- examples: t.field({
188
- type: [ExampleItem],
189
- resolve: (parent) =>
190
- Object.entries(parent.examples ?? {}).map(([name, value]) => ({
191
- name,
192
- ...value,
193
- })),
194
- nullable: true,
195
- }),
196
- schema: t.expose("schema", { type: JSONScalar, nullable: true }),
197
- }),
198
- });
199
-
200
- const MediaTypeItem = builder
201
- .objectRef<{
202
- mediaType: string;
203
- schema: any;
204
- examples: Array<ExampleObject & { name: string }>;
205
- encoding?: Array<EncodingObject & { name: string }>;
206
- }>("MediaTypeObject")
207
- .implement({
208
- fields: (t) => ({
209
- mediaType: t.exposeString("mediaType"),
210
- schema: t.expose("schema", { type: JSONScalar }),
211
- examples: t.expose("examples", { type: [ExampleItem], nullable: true }),
212
- encoding: t.expose("encoding", { type: [EncodingItem], nullable: true }),
213
- }),
214
- });
215
-
216
- const RequestBodyObject = builder
217
- .objectRef<{
218
- description?: string;
219
- required?: boolean;
220
- content: Array<{
221
- mediaType: string;
222
- schema: any;
223
- examples: Array<ExampleObject & { name: string }>;
224
- encoding?: Array<EncodingObject & { name: string }>;
225
- }>;
226
- }>("RequestBodyObject")
227
- .implement({
228
- fields: (t) => ({
229
- description: t.exposeString("description", { nullable: true }),
230
- content: t.expose("content", { type: [MediaTypeItem], nullable: true }),
231
- required: t.exposeBoolean("required", { nullable: true }),
232
- }),
233
- });
234
-
235
- const ResponseItem = builder
236
- .objectRef<{
237
- statusCode: string;
238
- description: string;
239
- content: Array<{
240
- mediaType: string;
241
- schema: any;
242
- examples: Array<ExampleObject & { name: string }>;
243
- }>;
244
- headers?: any;
245
- links?: any;
246
- }>("ResponseItem")
247
- .implement({
248
- fields: (t) => ({
249
- statusCode: t.exposeString("statusCode"),
250
- description: t.exposeString("description"),
251
- content: t.expose("content", { type: [MediaTypeItem], nullable: true }),
252
- headers: t.expose("headers", { type: JSONScalar, nullable: true }),
253
- links: t.expose("links", { type: JSONScalar, nullable: true }),
254
- }),
255
- });
256
-
257
- const OperationItem = builder
258
- .objectRef<
259
- OperationObject & { path: string; method: string; slug: string }
260
- >("OperationItem")
261
- .implement({
262
- fields: (t) => ({
263
- slug: t.exposeString("slug"),
264
- path: t.exposeString("path"),
265
- method: t.exposeString("method"),
266
- operationId: t.exposeString("operationId", { nullable: true }),
267
- summary: t.exposeString("summary", { nullable: true }),
268
- description: t.exposeString("description", { nullable: true }),
269
- contentTypes: t.stringList({
270
- resolve: (parent) => Object.keys(parent.requestBody?.content ?? {}),
271
- }),
272
- parameters: t.expose("parameters", {
273
- type: [ParameterItem],
274
- nullable: true,
275
- }),
276
- requestBody: t.field({
277
- type: RequestBodyObject,
278
- resolve: (parent) => ({
279
- description: parent.requestBody?.description,
280
- required: parent.requestBody?.required,
281
- content: Object.entries(parent.requestBody?.content ?? {}).map(
282
- ([mediaType, content]) => ({
283
- mediaType,
284
- schema: content.schema,
285
- examples: Object.entries(content.examples ?? {}).map(
286
- ([name, value]) => ({ name, ...value }),
287
- ),
288
- encoding: Object.entries(content.encoding ?? {}).map(
289
- ([name, value]) => ({ name, ...value }),
290
- ),
291
- }),
292
- ),
293
- }),
294
- nullable: true,
295
- }),
296
- responses: t.field({
297
- type: [ResponseItem],
298
- resolve: (parent) => {
299
- return Object.entries(parent.responses ?? {}).map(
300
- ([statusCode, response]) => ({
301
- statusCode,
302
- description: response.description,
303
- content: Object.entries(response.content ?? {}).map(
304
- ([mediaType, mediaTypeObject]) => ({
305
- mediaType,
306
- schema: mediaTypeObject.schema,
307
- examples: Object.entries(mediaTypeObject.examples ?? {}).map(
308
- ([name, value]) => ({ name, ...value }),
309
- ),
310
- }),
311
- ),
312
- headers: response.headers,
313
- links: response.links,
314
- }),
315
- );
316
- },
317
- }),
318
- tags: t.field({
319
- type: [TagItem],
320
- resolve: (parent, _, ctx) =>
321
- parent.tags?.map((tag) => ({
322
- name: tag,
323
- description: ctx.schema.tags?.find((t) => t.name === tag)
324
- ?.description,
325
- })),
326
- nullable: true,
327
- }),
328
- deprecated: t.exposeBoolean("deprecated", { nullable: true }),
329
- }),
330
- });
331
-
332
- const Schema = builder.objectRef<OpenAPIDocument>("Schema").implement({
333
- fields: (t) => ({
334
- openapi: t.string({ resolve: (root) => root.openapi }),
335
- url: t.string({ resolve: (root) => root.servers?.at(0)?.url ?? "/" }),
336
- title: t.string({ resolve: (root) => root.info.title }),
337
- version: t.string({ resolve: (root) => root.info.version }),
338
- description: t.string({
339
- resolve: (root) => root.info.description,
340
- nullable: true,
341
- }),
342
- paths: t.field({
343
- type: [PathItem],
344
- resolve: (root) =>
345
- Object.entries(root.paths ?? {}).map(([path, value]) => ({
346
- path,
347
- methods: Object.keys(value!) as typeof HttpMethods,
348
- })),
349
- }),
350
- tags: t.field({
351
- args: {
352
- name: t.arg.string(),
353
- },
354
- type: [SchemaTag],
355
- resolve: (root, args) =>
356
- [...(root.tags ?? []), { name: "" }].filter(
357
- (tag) => !args.name || args.name === tag.name,
358
- ),
359
- }),
360
- operations: t.field({
361
- type: [OperationItem],
362
- args: {
363
- path: t.arg.string(),
364
- method: t.arg.string(),
365
- operationId: t.arg.string(),
366
- tag: t.arg.string(),
367
- },
368
- resolve: (parent, args) =>
369
- getAllOperations(parent.paths).filter(
370
- (item) =>
371
- (!args.operationId || item.operationId === args.operationId) &&
372
- (!args.path || item.path === args.path) &&
373
- (!args.method || item.method === args.method) &&
374
- (!args.tag || item.tags?.includes(args.tag)),
375
- ),
376
- }),
377
- }),
378
- });
379
-
380
- const loadOpenAPISchema = async (input: NonNullable<unknown>) => {
381
- const hash = hashit(input);
382
-
383
- if (cache.has(hash)) {
384
- return cache.get(hash)!;
385
- }
386
-
387
- const schema = await validate(input);
388
-
389
- cache.set(hash, schema);
390
-
391
- return schema;
392
- };
393
-
394
- const SchemaSource = builder.enumType("SchemaType", {
395
- values: ["url", "json", "yaml"] as const,
396
- });
397
-
398
- builder.queryType({
399
- fields: (t) => ({
400
- // https://tan-cow-main-bce8a06.d2.zuplo.dev/openapi
401
- schema: t.field({
402
- type: Schema,
403
- args: {
404
- type: t.arg({ type: SchemaSource, required: true }),
405
- input: t.arg({ type: JSONScalar, required: true }),
406
- },
407
- resolve: async (_, args, ctx) => {
408
- const schema = await loadOpenAPISchema(args.input!);
409
- // for easier access of the whole schema in children resolvers
410
- ctx.schema = schema;
411
-
412
- return schema;
413
- },
414
- }),
415
- }),
416
- });
417
-
418
- export const schema = builder.toSchema();
419
-
420
- export const createGraphQLServer = (
421
- options?: Omit<YogaServerOptions<any, any>, "schema">,
422
- ) => createYoga({ schema, ...options });
@@ -1,10 +0,0 @@
1
- import { createServer } from "node:http";
2
- import { createGraphQLServer } from "./index.js";
3
-
4
- const yoga = createGraphQLServer();
5
-
6
- const server = createServer(yoga);
7
-
8
- server.listen(4000, () => {
9
- console.log(`🚀 Server ready at http://localhost:4000/graphql`);
10
- });