@xyd-js/apidocs-demo 0.0.0-build

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.
@@ -0,0 +1,538 @@
1
+ import {OpenAPIV3} from "openapi-types";
2
+
3
+ import type {
4
+ CodeBlockTab,
5
+ Example,
6
+ ExampleGroup,
7
+ Reference,
8
+ UniformPluginArgs,
9
+ OpenAPIReferenceContext
10
+ } from "@xyd-js/uniform";
11
+
12
+ interface NavigationGroup {
13
+ id: string
14
+
15
+ title: string
16
+
17
+ beta: boolean
18
+ }
19
+
20
+ interface Group {
21
+ id: string
22
+
23
+ title: string
24
+
25
+ description: string
26
+
27
+ navigationGroup: string
28
+
29
+ sections: GroupSection[]
30
+ }
31
+
32
+ interface GroupSection {
33
+ type: "endpoint" | "object"
34
+
35
+ key: string
36
+
37
+ path: "object" | "list" | "<auto>"
38
+ }
39
+
40
+ interface ComponentMeta {
41
+ name: string
42
+
43
+ group: string
44
+
45
+ example: string
46
+ }
47
+
48
+ interface oAiMeta {
49
+ navigationGroups: NavigationGroup[]
50
+
51
+ groups: Group[]
52
+ }
53
+
54
+ type ExtensionSchema = OpenAPIV3.Document & {
55
+ "x-oaiMeta"?: oAiMeta
56
+ }
57
+
58
+ type NavigationGroupMap = {
59
+ [id: string]: NavigationGroup & {
60
+ index: number
61
+ }
62
+ }
63
+
64
+ interface OperationExample {
65
+ request: string | { [lang: string]: string }
66
+ response: string | { [lang: string]: string }
67
+ }
68
+
69
+ type Examples = string | OperationExample | OperationExample[]
70
+
71
+ export function uniformOpenAIMeta({
72
+ references,
73
+ defer,
74
+ }: UniformPluginArgs) {
75
+ // TODO: in the future better api to get uniform specific data at beginning (e.g openapi schema)
76
+ let schema: ExtensionSchema | undefined
77
+ const refByOperationId: {
78
+ [key: string]: Reference
79
+ } = {}
80
+ const refByComponentSchema: {
81
+ [key: string]: Reference
82
+ } = {}
83
+
84
+ defer(() => {
85
+ // @ts-ignore
86
+ if (typeof references.__internal_options === "function") {
87
+ // @ts-ignore
88
+ const options = references.__internal_options()
89
+
90
+ if (options?.regions?.length) {
91
+ return {}
92
+ }
93
+ }
94
+
95
+ const output: Reference[] = []
96
+ if (!schema) {
97
+ return {}
98
+ }
99
+
100
+ const oaiMeta = schema["x-oaiMeta"]
101
+
102
+ if (!oaiMeta) {
103
+ return {}
104
+ }
105
+
106
+ const navigationMap: NavigationGroupMap = {}
107
+
108
+ for (let i in oaiMeta.navigationGroups || []) {
109
+ const navGroup = oaiMeta.navigationGroups[i]
110
+
111
+ if (!navGroup) {
112
+ continue
113
+ }
114
+
115
+ navigationMap[navGroup.id] = {
116
+ ...navGroup,
117
+ index: parseInt(i, 10)
118
+ }
119
+ }
120
+
121
+ for (const group of oaiMeta.groups) {
122
+ const navGroup = navigationMap[group.navigationGroup]
123
+ if (!navGroup) {
124
+ console.warn(`No navigation group found for group: ${group.id}`)
125
+ continue
126
+ }
127
+
128
+ if (!Array.isArray(group.sections)) {
129
+ continue
130
+ }
131
+
132
+ for (const section of group.sections) {
133
+ let uniformRef: Reference | undefined
134
+
135
+ switch (section.type) {
136
+ case "endpoint": {
137
+ const operationRef = refByOperationId[section.key]
138
+ if (!operationRef) {
139
+ console.warn(`No operation found for key: ${section.key} in group ${group.id}`)
140
+ break
141
+ }
142
+
143
+ uniformRef = operationRef
144
+
145
+ break
146
+ }
147
+
148
+ case "object": {
149
+ const componentRef = refByComponentSchema[section.key]
150
+ if (!componentRef) {
151
+ console.warn(`No component schema found for key: ${section.key} in group ${group.id}`)
152
+ break
153
+ }
154
+
155
+ const component = componentRef.__UNSAFE_selector("[component]") as OpenAPIV3.SchemaObject | undefined
156
+ if (!component) {
157
+ console.warn(`No component schema found for key: ${section.key} in group ${group.id}`)
158
+ break
159
+ }
160
+
161
+ let componentMeta: ComponentMeta | undefined
162
+ if (component.allOf) {
163
+ let found = false
164
+ for (const item of component.allOf) {
165
+ const oAiMeta = item["x-oaiMeta"] as ComponentMeta | undefined
166
+
167
+ if (oAiMeta && found) {
168
+ console.warn(`Multiple x-oaiMeta found in allOf for component schema: ${section.key} ingroup ${group.id}`)
169
+ }
170
+
171
+ if (oAiMeta) {
172
+ found = true
173
+ componentMeta = oAiMeta
174
+ break
175
+ }
176
+ }
177
+
178
+ if (!found) {
179
+ console.warn(`No x-oaiMeta found in allOf for component schema: ${section.key} in group ${group.id}`)
180
+ break
181
+ }
182
+
183
+ } else {
184
+ const oAiMeta = component["x-oaiMeta"] as ComponentMeta | undefined
185
+ if (!oAiMeta) {
186
+ console.warn(`No x-oaiMeta found for component schema: ${section.key} in group ${group.id}`)
187
+ break
188
+ }
189
+
190
+ componentMeta = oAiMeta
191
+ }
192
+
193
+ if (!componentMeta) {
194
+ console.warn(`No component meta found for key: ${section.key} in group ${group.id}`)
195
+ break
196
+ }
197
+
198
+ componentRef.title = componentMeta.name || componentRef.title
199
+ uniformRef = componentRef
200
+
201
+ if (componentMeta.example) {
202
+ const exampleGroups = oasOpenAiExamples(componentMeta.example)
203
+
204
+ uniformRef.examples = {
205
+ groups: exampleGroups,
206
+ }
207
+ }
208
+
209
+ break
210
+ }
211
+
212
+ default: {
213
+ uniformRef = undefined
214
+ console.warn(`Unknown section type: ${section.type} in group ${group.id}`)
215
+ continue
216
+ }
217
+ }
218
+
219
+ if (!uniformRef) {
220
+ continue
221
+ }
222
+
223
+ if (section.path && section.path !== "<auto>") {
224
+ uniformRef.canonical = `${group.id}/${section.path}`
225
+ }
226
+
227
+ if (!uniformRef.context) {
228
+ uniformRef.context = {}
229
+ }
230
+
231
+ uniformRef.context.group = [
232
+ navGroup.title,
233
+ group.title
234
+ ]
235
+
236
+ output.push(uniformRef)
237
+ }
238
+ }
239
+
240
+ // Clear references and set from output
241
+ if (Array.isArray(references)) {
242
+ references.length = 0
243
+ references.push(...output)
244
+ } else {
245
+ references = output[0] || references
246
+ }
247
+
248
+ return {}
249
+ })
250
+
251
+ return function pluginOpenAIMetaInner(ref: Reference) {
252
+ /// TODO: !!!! BETTER !!! MORE STREAM LIKE
253
+ // @ts-ignore
254
+ const selector = ref.__UNSAFE_selector
255
+ const oapSchema = selector("[schema]")
256
+ if (!oapSchema) {
257
+ return
258
+ }
259
+ schema = oapSchema
260
+
261
+ const ctx = ref.context as OpenAPIReferenceContext | undefined
262
+ if (ctx?.componentSchema) {
263
+ refByComponentSchema[ctx.componentSchema] = ref
264
+ }
265
+
266
+ if (!selector || typeof selector !== "function") {
267
+ return
268
+ }
269
+
270
+ const methodPath = selector("[method] [path]") as OpenAPIV3.OperationObject | undefined
271
+ if (!methodPath) {
272
+ return
273
+ }
274
+
275
+ const oapMethod = selector("[method]")
276
+ if (!oapMethod) {
277
+ return
278
+ }
279
+
280
+ const meta = methodPath["x-oaiMeta"]
281
+ if (!meta) {
282
+ return
283
+ }
284
+
285
+ if (meta.name) {
286
+ ref.title = meta.name
287
+ }
288
+
289
+ if (meta.group) {
290
+ if (ref.context) {
291
+ ref.context.group = [meta.group]
292
+ }
293
+ }
294
+
295
+ if (!ref.description) {
296
+ ref.description = methodPath.summary || ""
297
+ }
298
+
299
+ if (meta.examples) {
300
+ const exampleGroups = oasOpenAiExamples(meta.examples)
301
+
302
+ ref.examples = {
303
+ groups: exampleGroups,
304
+ }
305
+ }
306
+
307
+ if (meta.returns) {
308
+ if (ref.definitions?.length) {
309
+ ref.definitions[ref.definitions.length - 1] = {
310
+ title: ref.definitions[ref.definitions.length - 1].title,
311
+ description: meta.returns,
312
+ properties: []
313
+ }
314
+ } else {
315
+ ref.definitions = [
316
+ {
317
+ title: "Response",
318
+ description: meta.returns,
319
+ properties: []
320
+ }
321
+ ]
322
+ }
323
+ }
324
+
325
+ refByOperationId[methodPath.operationId || ""] = ref
326
+ }
327
+ }
328
+
329
+ function oasOpenAiExamples(examples: Examples) {
330
+ const groups: ExampleGroup[] = []
331
+
332
+ if (examples) {
333
+ if (Array.isArray(examples)) {
334
+ // Create request group
335
+ const requestExamples: Example[] = []
336
+ examples.forEach((example: {
337
+ title?: string;
338
+ request?: string | Record<string, string>;
339
+ response?: string | Record<string, string>;
340
+ }) => {
341
+ if (example.request) {
342
+ const tabs: CodeBlockTab[] = []
343
+ if (typeof example.request === "string") {
344
+ tabs.push({
345
+ title: "",
346
+ language: "json",
347
+ code: example.request
348
+ })
349
+ } else {
350
+ for (let lang of Object.keys(example.request)) {
351
+ const code = example.request[lang] || ""
352
+ const language = lang === "curl" ? "bash" :
353
+ lang === "node.js" ? "js" : lang
354
+
355
+ tabs.push({
356
+ title: lang,
357
+ language,
358
+ code
359
+ })
360
+ }
361
+ }
362
+ if (tabs.length > 0) {
363
+ requestExamples.push({
364
+ description: example.title || "",
365
+ codeblock: {
366
+ title: example.title || "",
367
+ tabs
368
+ }
369
+ })
370
+ }
371
+ }
372
+ })
373
+ if (requestExamples.length > 0) {
374
+ groups.push({
375
+ description: "Example request",
376
+ examples: requestExamples
377
+ })
378
+ }
379
+
380
+ // Create response group
381
+ const responseExamples: Example[] = []
382
+ examples.forEach((example: {
383
+ title?: string;
384
+ request?: string | Record<string, string>;
385
+ response?: string | Record<string, string>;
386
+ }) => {
387
+ if (example.response) {
388
+ const tabs: CodeBlockTab[] = []
389
+ if (typeof example.response === "string") {
390
+ tabs.push({
391
+ title: "",
392
+ language: "json",
393
+ code: example.response
394
+ })
395
+ } else {
396
+ for (let lang of Object.keys(example.response)) {
397
+ const code = example.response[lang] || ""
398
+ const language = lang === "curl" ? "bash" :
399
+ lang === "node.js" ? "js" : lang
400
+
401
+ tabs.push({
402
+ title: lang,
403
+ language,
404
+ code
405
+ })
406
+ }
407
+ }
408
+ if (tabs.length > 0) {
409
+ responseExamples.push({
410
+ description: example.title || "",
411
+ codeblock: {
412
+ title: example.title || "",
413
+ tabs
414
+ }
415
+ })
416
+ }
417
+ }
418
+ })
419
+ if (responseExamples.length > 0) {
420
+ groups.push({
421
+ description: "Example response",
422
+ examples: responseExamples
423
+ })
424
+ }
425
+ } else {
426
+ if (typeof examples === "string") {
427
+ groups.push({
428
+ description: "Example",
429
+ examples: [
430
+ {
431
+ description: "",
432
+ codeblock: {
433
+ tabs: [
434
+ {
435
+ title: "",
436
+ language: "json",
437
+ code: examples
438
+ }
439
+ ]
440
+ }
441
+ }
442
+ ]
443
+ })
444
+ } else {
445
+ if (examples.request) {
446
+ const tabs: CodeBlockTab[] = []
447
+
448
+ if (typeof examples.request === "string") {
449
+ tabs.push({
450
+ title: "",
451
+ language: "json",
452
+ code: examples.request || "",
453
+ })
454
+ } else {
455
+ for (let lang of Object.keys(examples.request)) {
456
+ const code = examples.request[lang] || ""
457
+
458
+ switch (lang) {
459
+ case "curl":
460
+ lang = "bash"
461
+ break
462
+ case "node.js":
463
+ lang = "js"
464
+ break
465
+ default:
466
+ break
467
+ }
468
+
469
+ tabs.push({
470
+ title: lang,
471
+ language: lang,
472
+ code,
473
+ })
474
+ }
475
+ }
476
+
477
+ groups.push({
478
+ description: "Example request",
479
+ examples: [
480
+ {
481
+ description: "",
482
+ codeblock: {
483
+ tabs
484
+ }
485
+ }
486
+ ]
487
+ })
488
+ }
489
+
490
+ if (examples.response) {
491
+ const tabs: CodeBlockTab[] = []
492
+ if (typeof examples.response === "string") {
493
+ tabs.push({
494
+ title: "",
495
+ language: "json",
496
+ code: examples.response || "",
497
+ })
498
+ } else {
499
+ for (let lang of Object.keys(examples.response)) {
500
+ const code = examples.response[lang] || ""
501
+
502
+ switch (lang) {
503
+ case "curl":
504
+ lang = "bash"
505
+ break
506
+ case "node.js":
507
+ lang = "js"
508
+ break
509
+ default:
510
+ break
511
+ }
512
+
513
+ tabs.push({
514
+ title: lang,
515
+ language: lang,
516
+ code,
517
+ })
518
+ }
519
+ }
520
+
521
+ groups.push({
522
+ description: "Example response",
523
+ examples: [
524
+ {
525
+ description: "",
526
+ codeblock: {
527
+ tabs
528
+ }
529
+ }
530
+ ]
531
+ })
532
+ }
533
+ }
534
+ }
535
+ }
536
+
537
+ return groups
538
+ }
package/app/api/try.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { toUniform } from "~/utils/toUniform";
2
+
3
+ import type { Route } from "../+types/root";
4
+
5
+ const prefix = "/docs/api" // TODO: IN THE FUTURE BETTER cuz should also work without prefix
6
+
7
+ export async function action({
8
+ request,
9
+ }: Route.ClientActionArgs) {
10
+ const formData = await request.formData();
11
+ const example = (formData.get("example") as string || "").trim()
12
+ let type = (formData.get("type") as string || "").trim()
13
+ const value = (formData.get("value") as string || "").trim()
14
+ const currentSettings = formData.get("currentSettings") as string
15
+
16
+ let parsedSettings = null;
17
+ if (currentSettings) {
18
+ try {
19
+ parsedSettings = JSON.parse(currentSettings);
20
+ } catch (error) {
21
+ console.error('Error parsing current settings:', error);
22
+ }
23
+ }
24
+
25
+ return toUniform(prefix, example, type, value, parsedSettings)
26
+ }
package/app/const.ts ADDED
@@ -0,0 +1,2 @@
1
+ // TODO: IN THE FUTURE BETTER cuz should also work without prefix
2
+ export const DOCS_PREFIX = "/docs/api"
@@ -0,0 +1,52 @@
1
+ import { createContext, useContext, useState, useEffect } from 'react';
2
+ import type { Reference } from '@xyd-js/uniform';
3
+ import type { Settings } from '@xyd-js/core';
4
+ import type { FwSidebarItemProps } from '@xyd-js/framework/react';
5
+ import { SETTINGS } from './settings';
6
+
7
+ interface GlobalStateActionData {
8
+ references: Reference[];
9
+ settings: Settings
10
+ groups: FwSidebarItemProps[]
11
+ exampleType: "openapi" | "graphql"
12
+ }
13
+
14
+ // Create a context for the global state
15
+ export const GlobalStateContext = createContext<{
16
+ actionData: GlobalStateActionData | null;
17
+ setActionData: (data: GlobalStateActionData) => void;
18
+ }>({
19
+ actionData: null,
20
+ setActionData: () => { },
21
+ });
22
+
23
+ // Create a provider component
24
+ export function GlobalStateProvider({ children }: { children: React.ReactNode }) {
25
+ const [actionData, setActionData] = useState<any>({
26
+ references: [],
27
+ settings: SETTINGS,
28
+ groups: [],
29
+ exampleType: ""
30
+ });
31
+
32
+ const handleSetActionData = (data: any) => {
33
+ setActionData(data);
34
+ };
35
+
36
+ return (
37
+ <GlobalStateContext.Provider value={{ actionData, setActionData: handleSetActionData }}>
38
+ {children}
39
+ </GlobalStateContext.Provider>
40
+ );
41
+ }
42
+
43
+ // Custom hook for using the global state
44
+ export function useGlobalState() {
45
+ const context = useContext(GlobalStateContext);
46
+ if (context === undefined) {
47
+ throw new Error('useGlobalState must be used within a GlobalStateProvider');
48
+ }
49
+ return context;
50
+ }
51
+
52
+ export const UrlContext = createContext({})
package/app/index.css ADDED
@@ -0,0 +1,58 @@
1
+ :root {
2
+ --xyd-nav-height: 0px !important;
3
+ }
4
+ [data-color-scheme="dark"] {
5
+ --color-text-default: var(--black);
6
+ }
7
+ /* System Dark Mode Support */
8
+ @media (prefers-color-scheme: dark) {
9
+ :root:not([data-color-scheme="light"]):not([data-color-scheme="dark"]) {
10
+ --color-text-default: var(--black);
11
+ }
12
+ }
13
+
14
+ /* FIX IMPORANT */
15
+ xyd-layout-primary {
16
+ [part=sidebar], atlas-apiref-samples {
17
+ top: 50px !important;
18
+ }
19
+ }
20
+
21
+
22
+ xyd-banner {
23
+ background: none;
24
+ border-bottom: 1px solid var(--dark32);
25
+ justify-content: normal;
26
+ }
27
+
28
+ xyd-layout-primary > header {
29
+ display: none
30
+ }
31
+ xyd-layout-primary > main {
32
+ top: 100px
33
+ }
34
+
35
+ .banner-container {
36
+ display: flex;
37
+ flex-direction: row;
38
+ justify-content: space-between;
39
+ align-items: center;
40
+ width: 100%;
41
+ position: fixed;
42
+ top: 0;
43
+ z-index: 5;
44
+ background: var(--white);
45
+ border-bottom: 1px solid var(--dark32);
46
+ padding: 15px;
47
+
48
+ .banner-left {
49
+ display: flex;
50
+ align-items: center;
51
+ gap: 10px;
52
+ padding-left: 20px;
53
+ }
54
+
55
+ .banner-right {
56
+ padding-right: 20px;
57
+ }
58
+ }