@xyd-js/openapi 0.1.0-xyd.11 → 0.1.0-xyd.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 (49) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/__fixtures__/-2.complex.openai/input.yaml +39848 -0
  3. package/__fixtures__/-2.complex.openai/output.json +321646 -0
  4. package/__fixtures__/-2.complex.openai/pluginOasOpenai.ts +553 -0
  5. package/__fixtures__/1.basic/input.yaml +226 -0
  6. package/__fixtures__/1.basic/output.json +1919 -0
  7. package/__fixtures__/2.more/input.yaml +76 -0
  8. package/__fixtures__/2.more/output.json +292 -0
  9. package/__fixtures__/3.multiple-responses/input.yaml +48 -0
  10. package/__fixtures__/3.multiple-responses/output.json +266 -0
  11. package/__fixtures__/4.abc/input.yaml +639 -0
  12. package/__fixtures__/4.abc/output.json +3828 -0
  13. package/__fixtures__/5.xdocs.codeLanguages/input.yaml +231 -0
  14. package/__fixtures__/5.xdocs.codeLanguages/output.json +1879 -0
  15. package/__fixtures__/5.xdocs.sidebar/input.yaml +256 -0
  16. package/__fixtures__/5.xdocs.sidebar/output.json +843 -0
  17. package/__fixtures__/6.codeSamples/input.yaml +75 -0
  18. package/__fixtures__/6.codeSamples/output.json +293 -0
  19. package/__tests__/oapSchemaToReferences.test.ts +88 -0
  20. package/__tests__/utils.ts +81 -0
  21. package/dist/index.cjs +1859 -162
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +36 -4
  24. package/dist/index.d.ts +36 -4
  25. package/dist/index.js +1855 -155
  26. package/dist/index.js.map +1 -1
  27. package/index.ts +10 -2
  28. package/package.json +11 -6
  29. package/src/const.ts +5 -1
  30. package/src/converters/oas-componentSchemas.ts +205 -0
  31. package/src/converters/oas-examples.ts +417 -0
  32. package/src/{parameters.ts → converters/oas-parameters.ts} +17 -3
  33. package/src/converters/oas-paths.ts +354 -0
  34. package/src/{requestBody.ts → converters/oas-requestBody.ts} +30 -10
  35. package/src/converters/oas-responses.ts +76 -0
  36. package/src/converters/oas-schema.ts +141 -0
  37. package/src/index.ts +13 -5
  38. package/src/oas-core.ts +579 -0
  39. package/src/types.ts +18 -0
  40. package/src/utils.ts +103 -90
  41. package/src/xdocs/index.ts +18 -0
  42. package/src/xdocs/pluginSidebar.ts +580 -0
  43. package/src/xdocs/types.ts +26 -0
  44. package/vitest.config.ts +7 -0
  45. package/src/examples.ts +0 -116
  46. package/src/paths.ts +0 -103
  47. package/src/properties.ts +0 -37
  48. package/src/responses.ts +0 -38
  49. package/src/schema.ts +0 -62
@@ -0,0 +1,553 @@
1
+ import {OpenAPIV3} from "openapi-types";
2
+
3
+ import { Settings } from "@xyd-js/core";
4
+ import {
5
+ CodeBlockTab,
6
+ Example,
7
+ ExampleGroup,
8
+ Reference,
9
+ UniformPluginArgs,
10
+ OpenAPIReferenceContext
11
+ } from "@xyd-js/uniform";
12
+ import type { Plugin, PluginConfig } from "@xyd-js/plugins";
13
+
14
+ export default function pluginOasOpenai(settings: Settings): PluginConfig {
15
+ return {
16
+ name: "oas-openai",
17
+ uniform: [
18
+ uniformOpenAIMeta,
19
+ ]
20
+ }
21
+ }
22
+
23
+
24
+ interface NavigationGroup {
25
+ id: string
26
+
27
+ title: string
28
+
29
+ beta: boolean
30
+ }
31
+
32
+ interface Group {
33
+ id: string
34
+
35
+ title: string
36
+
37
+ description: string
38
+
39
+ navigationGroup: string
40
+
41
+ sections: GroupSection[]
42
+ }
43
+
44
+ interface GroupSection {
45
+ type: "endpoint" | "object"
46
+
47
+ key: string
48
+
49
+ path: "object" | "list" | "<auto>"
50
+ }
51
+
52
+ interface ComponentMeta {
53
+ name: string
54
+
55
+ group: string
56
+
57
+ example: string
58
+ }
59
+
60
+ interface oAiMeta {
61
+ navigationGroups: NavigationGroup[]
62
+
63
+ groups: Group[]
64
+ }
65
+
66
+ type ExtensionSchema = OpenAPIV3.Document & {
67
+ "x-oaiMeta"?: oAiMeta
68
+ }
69
+
70
+ type NavigationGroupMap = {
71
+ [id: string]: NavigationGroup & {
72
+ index: number
73
+ }
74
+ }
75
+
76
+ interface OperationExample {
77
+ request: string | { [lang: string]: string }
78
+ response: string | { [lang: string]: string }
79
+ }
80
+
81
+ type Examples = string | OperationExample | OperationExample[]
82
+
83
+ export function uniformOpenAIMeta({
84
+ references,
85
+ defer,
86
+ }: UniformPluginArgs) {
87
+ // TODO: in the future better api to get uniform specific data at beginning (e.g openapi schema)
88
+ let schema: ExtensionSchema | undefined
89
+ const refByOperationId: {
90
+ [key: string]: Reference
91
+ } = {}
92
+ const refByComponentSchema: {
93
+ [key: string]: Reference
94
+ } = {}
95
+
96
+ defer(() => {
97
+ // @ts-ignore
98
+ if (typeof references.__internal_options === "function") {
99
+ // @ts-ignore
100
+ const options = references.__internal_options()
101
+
102
+ if (options?.regions?.length) {
103
+ return {}
104
+ }
105
+ }
106
+
107
+ const output: Reference[] = []
108
+ if (!schema) {
109
+ return {}
110
+ }
111
+
112
+ const oaiMeta = schema["x-oaiMeta"]
113
+
114
+ if (!oaiMeta) {
115
+ return {}
116
+ }
117
+
118
+ const navigationMap: NavigationGroupMap = {}
119
+
120
+ for (let i in oaiMeta.navigationGroups || []) {
121
+ const navGroup = oaiMeta.navigationGroups[i]
122
+
123
+ if (!navGroup) {
124
+ continue
125
+ }
126
+
127
+ navigationMap[navGroup.id] = {
128
+ ...navGroup,
129
+ index: parseInt(i, 10)
130
+ }
131
+ }
132
+
133
+ for (const group of oaiMeta.groups) {
134
+ const navGroup = navigationMap[group.navigationGroup]
135
+ if (!navGroup) {
136
+ console.warn(`No navigation group found for group: ${group.id}`)
137
+ continue
138
+ }
139
+
140
+ if (!Array.isArray(group.sections)) {
141
+ continue
142
+ }
143
+
144
+ for (const section of group.sections) {
145
+ let uniformRef: Reference | undefined
146
+
147
+ switch (section.type) {
148
+ case "endpoint": {
149
+ const operationRef = refByOperationId[section.key]
150
+ if (!operationRef) {
151
+ console.warn(`No operation found for key: ${section.key} in group ${group.id}`)
152
+ break
153
+ }
154
+
155
+ uniformRef = operationRef
156
+
157
+ break
158
+ }
159
+
160
+ case "object": {
161
+ const componentRef = refByComponentSchema[section.key]
162
+ if (!componentRef) {
163
+ console.warn(`No component schema found for key: ${section.key} in group ${group.id}`)
164
+ break
165
+ }
166
+
167
+ const component = componentRef.__UNSAFE_selector("[component]") as OpenAPIV3.SchemaObject | undefined
168
+ if (!component) {
169
+ console.warn(`No component schema found for key: ${section.key} in group ${group.id}`)
170
+ break
171
+ }
172
+
173
+ let componentMeta: ComponentMeta | undefined
174
+ if (component.allOf) {
175
+ let found = false
176
+ for (const item of component.allOf) {
177
+ const oAiMeta = item["x-oaiMeta"] as ComponentMeta | undefined
178
+
179
+ if (oAiMeta && found) {
180
+ console.warn(`Multiple x-oaiMeta found in allOf for component schema: ${section.key} ingroup ${group.id}`)
181
+ }
182
+
183
+ if (oAiMeta) {
184
+ found = true
185
+ componentMeta = oAiMeta
186
+ break
187
+ }
188
+ }
189
+
190
+ if (!found) {
191
+ console.warn(`No x-oaiMeta found in allOf for component schema: ${section.key} in group ${group.id}`)
192
+ break
193
+ }
194
+
195
+ } else {
196
+ const oAiMeta = component["x-oaiMeta"] as ComponentMeta | undefined
197
+ if (!oAiMeta) {
198
+ console.warn(`No x-oaiMeta found for component schema: ${section.key} in group ${group.id}`)
199
+ break
200
+ }
201
+
202
+ componentMeta = oAiMeta
203
+ }
204
+
205
+ if (!componentMeta) {
206
+ console.warn(`No component meta found for key: ${section.key} in group ${group.id}`)
207
+ break
208
+ }
209
+
210
+ componentRef.title = componentMeta.name || componentRef.title
211
+ uniformRef = componentRef
212
+
213
+ if (componentMeta.example) {
214
+ const exampleGroups = oasOpenAiExamples(componentMeta.example)
215
+
216
+ uniformRef.examples = {
217
+ groups: exampleGroups,
218
+ }
219
+ }
220
+
221
+ break
222
+ }
223
+
224
+ default: {
225
+ uniformRef = undefined
226
+ console.warn(`Unknown section type: ${section.type} in group ${group.id}`)
227
+ continue
228
+ }
229
+ }
230
+
231
+ if (!uniformRef) {
232
+ continue
233
+ }
234
+
235
+ if (section.path && section.path !== "<auto>") {
236
+ uniformRef.canonical = `${group.id}/${section.path}`
237
+ }
238
+
239
+ if (!uniformRef.context) {
240
+ uniformRef.context = {}
241
+ }
242
+
243
+ uniformRef.context.group = [
244
+ navGroup.title,
245
+ group.title
246
+ ]
247
+
248
+ output.push(uniformRef)
249
+ }
250
+ }
251
+
252
+ // Clear references and set from output
253
+ if (Array.isArray(references)) {
254
+ references.length = 0
255
+ references.push(...output)
256
+ } else {
257
+ references = output[0] || references
258
+ }
259
+
260
+ return {}
261
+ })
262
+
263
+ return function pluginOpenAIMetaInner(ref: Reference) {
264
+ /// TODO: !!!! BETTER !!! MORE STREAM LIKE
265
+ // @ts-ignore
266
+ const selector = ref.__UNSAFE_selector
267
+ const oapSchema = selector("[schema]")
268
+ if (!oapSchema) {
269
+ return
270
+ }
271
+ schema = oapSchema
272
+
273
+ const ctx = ref.context as OpenAPIReferenceContext | undefined
274
+ if (ctx?.componentSchema) {
275
+ refByComponentSchema[ctx.componentSchema] = ref
276
+ }
277
+
278
+ if (!selector || typeof selector !== "function") {
279
+ return
280
+ }
281
+
282
+ const methodPath = selector("[method] [path]") as OpenAPIV3.OperationObject | undefined
283
+ if (!methodPath) {
284
+ return
285
+ }
286
+
287
+ const oapMethod = selector("[method]")
288
+ if (!oapMethod) {
289
+ return
290
+ }
291
+
292
+ const meta = methodPath["x-oaiMeta"]
293
+ if (!meta) {
294
+ return
295
+ }
296
+
297
+ if (meta.name) {
298
+ ref.title = meta.name
299
+ }
300
+
301
+ if (meta.group) {
302
+ if (ref.context) {
303
+ ref.context.group = [meta.group]
304
+ }
305
+ }
306
+
307
+ if (!ref.description) {
308
+ ref.description = methodPath.summary || ""
309
+ }
310
+
311
+ if (meta.examples) {
312
+ const exampleGroups = oasOpenAiExamples(meta.examples)
313
+
314
+ ref.examples = {
315
+ groups: exampleGroups,
316
+ }
317
+ }
318
+
319
+ if (meta.returns) {
320
+ if (ref.definitions?.length) {
321
+ ref.definitions[ref.definitions.length - 1] = {
322
+ title: ref.definitions[ref.definitions.length - 1].title,
323
+ description: meta.returns,
324
+ properties: []
325
+ }
326
+ } else {
327
+ ref.definitions = [
328
+ {
329
+ title: "Response",
330
+ description: meta.returns,
331
+ properties: []
332
+ }
333
+ ]
334
+ }
335
+ }
336
+
337
+ if (methodPath.operationId === "") {
338
+
339
+ }
340
+ refByOperationId[methodPath.operationId || ""] = ref
341
+ }
342
+ }
343
+
344
+ function oasOpenAiExamples(examples: Examples) {
345
+ const groups: ExampleGroup[] = []
346
+
347
+ if (examples) {
348
+ if (Array.isArray(examples)) {
349
+ // Create request group
350
+ const requestExamples: Example[] = []
351
+ examples.forEach((example: {
352
+ title?: string;
353
+ request?: string | Record<string, string>;
354
+ response?: string | Record<string, string>;
355
+ }) => {
356
+ if (example.request) {
357
+ const tabs: CodeBlockTab[] = []
358
+ if (typeof example.request === "string") {
359
+ tabs.push({
360
+ title: "",
361
+ language: "json",
362
+ code: example.request
363
+ })
364
+ } else {
365
+ for (let lang of Object.keys(example.request)) {
366
+ const code = example.request[lang] || ""
367
+ const language = lang === "curl" ? "bash" :
368
+ lang === "node.js" ? "js" : lang
369
+
370
+ tabs.push({
371
+ title: lang,
372
+ language,
373
+ code
374
+ })
375
+ }
376
+ }
377
+ if (tabs.length > 0) {
378
+ requestExamples.push({
379
+ description: example.title || "",
380
+ codeblock: {
381
+ title: example.title || "",
382
+ tabs
383
+ }
384
+ })
385
+ }
386
+ }
387
+ })
388
+ if (requestExamples.length > 0) {
389
+ groups.push({
390
+ description: "Example request",
391
+ examples: requestExamples
392
+ })
393
+ }
394
+
395
+ // Create response group
396
+ const responseExamples: Example[] = []
397
+ examples.forEach((example: {
398
+ title?: string;
399
+ request?: string | Record<string, string>;
400
+ response?: string | Record<string, string>;
401
+ }) => {
402
+ if (example.response) {
403
+ const tabs: CodeBlockTab[] = []
404
+ if (typeof example.response === "string") {
405
+ tabs.push({
406
+ title: "",
407
+ language: "json",
408
+ code: example.response
409
+ })
410
+ } else {
411
+ for (let lang of Object.keys(example.response)) {
412
+ const code = example.response[lang] || ""
413
+ const language = lang === "curl" ? "bash" :
414
+ lang === "node.js" ? "js" : lang
415
+
416
+ tabs.push({
417
+ title: lang,
418
+ language,
419
+ code
420
+ })
421
+ }
422
+ }
423
+ if (tabs.length > 0) {
424
+ responseExamples.push({
425
+ description: example.title || "",
426
+ codeblock: {
427
+ title: example.title || "",
428
+ tabs
429
+ }
430
+ })
431
+ }
432
+ }
433
+ })
434
+ if (responseExamples.length > 0) {
435
+ groups.push({
436
+ description: "Example response",
437
+ examples: responseExamples
438
+ })
439
+ }
440
+ } else {
441
+ if (typeof examples === "string") {
442
+ groups.push({
443
+ description: "Example",
444
+ examples: [
445
+ {
446
+ description: "",
447
+ codeblock: {
448
+ tabs: [
449
+ {
450
+ title: "",
451
+ language: "json",
452
+ code: examples
453
+ }
454
+ ]
455
+ }
456
+ }
457
+ ]
458
+ })
459
+ } else {
460
+ if (examples.request) {
461
+ const tabs: CodeBlockTab[] = []
462
+
463
+ if (typeof examples.request === "string") {
464
+ tabs.push({
465
+ title: "",
466
+ language: "json",
467
+ code: examples.request || "",
468
+ })
469
+ } else {
470
+ for (let lang of Object.keys(examples.request)) {
471
+ const code = examples.request[lang] || ""
472
+
473
+ switch (lang) {
474
+ case "curl":
475
+ lang = "bash"
476
+ break
477
+ case "node.js":
478
+ lang = "js"
479
+ break
480
+ default:
481
+ break
482
+ }
483
+
484
+ tabs.push({
485
+ title: lang,
486
+ language: lang,
487
+ code,
488
+ })
489
+ }
490
+ }
491
+
492
+ groups.push({
493
+ description: "Example request",
494
+ examples: [
495
+ {
496
+ description: "",
497
+ codeblock: {
498
+ tabs
499
+ }
500
+ }
501
+ ]
502
+ })
503
+ }
504
+
505
+ if (examples.response) {
506
+ const tabs: CodeBlockTab[] = []
507
+ if (typeof examples.response === "string") {
508
+ tabs.push({
509
+ title: "",
510
+ language: "json",
511
+ code: examples.response || "",
512
+ })
513
+ } else {
514
+ for (let lang of Object.keys(examples.response)) {
515
+ const code = examples.response[lang] || ""
516
+
517
+ switch (lang) {
518
+ case "curl":
519
+ lang = "bash"
520
+ break
521
+ case "node.js":
522
+ lang = "js"
523
+ break
524
+ default:
525
+ break
526
+ }
527
+
528
+ tabs.push({
529
+ title: lang,
530
+ language: lang,
531
+ code,
532
+ })
533
+ }
534
+ }
535
+
536
+ groups.push({
537
+ description: "Example response",
538
+ examples: [
539
+ {
540
+ description: "",
541
+ codeblock: {
542
+ tabs
543
+ }
544
+ }
545
+ ]
546
+ })
547
+ }
548
+ }
549
+ }
550
+ }
551
+
552
+ return groups
553
+ }